<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/blog/css/main.css?v=5.1.2" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="Hexo, NexT" />








  <link rel="shortcut icon" type="image/x-icon" href="/blog/favicon.ico?v=5.1.2" />






<meta property="og:type" content="website">
<meta property="og:title" content="个人博客">
<meta property="og:url" content="http://xiezixi.oschina.io/index.html">
<meta property="og:site_name" content="个人博客">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="个人博客">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Muse',
    sidebar: {"position":"left","display":"post","offset":12,"offset_float":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: true,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://xiezixi.oschina.io/"/>





  <title>个人博客</title>
  














</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
  
  
    
  

  <div class="container sidebar-position-left 
   page-home 
 ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">个人博客</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">曦瓜皮</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>


 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
  <section id="posts" class="posts-expand">
    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/07/ES6-Promise/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/07/ES6-Promise/" itemprop="url">ECMAScript6 之 Promise™</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-07T20:58:45+08:00">
                2018-06-07
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/ECMAScript6/" itemprop="url" rel="index">
                    <span itemprop="name">ECMAScript6</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="1-什么是-Promise"><a href="#1-什么是-Promise" class="headerlink" title="1. 什么是 Promise"></a>1. 什么是 Promise</h2><h3 id="1-1-定义"><a href="#1-1-定义" class="headerlink" title="1.1. 定义"></a>1.1. 定义</h3><ul>
<li>Promise 对象用于异步计算</li>
<li>一个 Promise 表示一个现在、将来或永不可能用到的值</li>
</ul>
<h3 id="1-2-优缺点"><a href="#1-2-优缺点" class="headerlink" title="1.2. 优缺点"></a>1.2. 优缺点</h3><h4 id="1-2-1-优点"><a href="#1-2-1-优点" class="headerlink" title="1.2.1. 优点"></a>1.2.1. 优点</h4><ul>
<li>可以将异步操作以同步操作的流程表达出来，避免了层层嵌套的回调函数</li>
<li>Promise 对象提供统一的接口，使得控制异步操作更加容易</li>
</ul>
<h4 id="1-2-2-缺点"><a href="#1-2-2-缺点" class="headerlink" title="1.2.2. 缺点"></a>1.2.2. 缺点</h4><ul>
<li>无法取消 Promise，一旦新建它就会立即执行，无法中途取消。</li>
<li>如果不设置回调函数，Promise 内部抛出的错误，不会反应到外部</li>
<li>当处于 Pending 状态时，无法得知目前进展到哪一个阶段</li>
</ul>
<h3 id="1-3-使用方法"><a href="#1-3-使用方法" class="headerlink" title="1.3. 使用方法"></a>1.3. 使用方法</h3><p>Promise 构造函数包含一个参数和一个带有 resolve（解析）和 reject（拒绝）两个参数的回调。在回调中执行一些操作（例如异步），如果一切都正常，则调用 resolve，否则调用 reject。</p>
<ul>
<li>Promise 状态发生变化，就会触发 <code>.then()</code> 里的响应函数处理后续步骤</li>
<li>Promise 状态最多只能改变一次，一经改变，不能再变<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(</span><br><span class="line">    <span class="comment">// 执行器 executor</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 一段耗时很长的异步操作</span></span><br><span class="line"></span><br><span class="line">        resolve(); <span class="comment">// 数据处理完成</span></span><br><span class="line"></span><br><span class="line">        reject(); <span class="comment">// 数据处理出错</span></span><br><span class="line">    &#125;</span><br><span class="line">).then(<span class="function"><span class="keyword">function</span> <span class="title">A</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 处理成功 下一步操作</span></span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span> <span class="title">B</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 处理失败 下一步操作</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
</li>
</ul>
<p><img src="http://xiezixi.oschina.io/blog/images/promise1.png" alt="promise"></p>
<h3 id="1-4-Promise-的-3-种状态"><a href="#1-4-Promise-的-3-种状态" class="headerlink" title="1.4. Promise 的 3 种状态"></a>1.4. Promise 的 3 种状态</h3><table>
<thead>
<tr>
<th>状态</th>
<th>含义</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>pending</code></td>
<td>待定</td>
<td>初始状态</td>
</tr>
<tr>
<td><code>fulfilled</code></td>
<td>实现</td>
<td>操作成功</td>
</tr>
<tr>
<td><code>rejected</code></td>
<td>被否决</td>
<td>操作失败</td>
</tr>
</tbody>
</table>
<h3 id="1-5-最简单的实例"><a href="#1-5-最简单的实例" class="headerlink" title="1.5. 最简单的实例"></a>1.5. 最简单的实例</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 先输出 here we go，等待 2s 后输出 hello world</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'here we go'</span>);</span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        resolve(<span class="string">'hello'</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;).then(<span class="function"><span class="params">value</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(value + <span class="string">' world'</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="1-6-使用-Promise-实现-Ajax"><a href="#1-6-使用-Promise-实现-Ajax" class="headerlink" title="1.6. 使用 Promise 实现 Ajax"></a>1.6. 使用 Promise 实现 Ajax</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ajax</span>(<span class="params">URL</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> req = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">        req.open(<span class="string">'GET'</span>, URL, <span class="literal">true</span>);</span><br><span class="line">        req.onload = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (req.status === <span class="number">200</span>) &#123;</span><br><span class="line">                resolve(req.responseText);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                reject(<span class="keyword">new</span> <span class="built_in">Error</span>(req.statusText));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        req.onerror = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            reject(<span class="keyword">new</span> <span class="built_in">Error</span>(req.statusText));</span><br><span class="line">        &#125;;</span><br><span class="line">        req.send();</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="1-7-浏览器兼容性"><a href="#1-7-浏览器兼容性" class="headerlink" title="1.7. 浏览器兼容性"></a>1.7. 浏览器兼容性</h3><p><a href="https://caniuse.com/#search=Promise" target="_blank" rel="noopener">Promise 浏览器兼容性</a></p>
<h2 id="2-异步的问题"><a href="#2-异步的问题" class="headerlink" title="2. 异步的问题"></a>2. 异步的问题</h2><h3 id="2-1-异步操作的常见语法"><a href="#2-1-异步操作的常见语法" class="headerlink" title="2.1. 异步操作的常见语法"></a>2.1. 异步操作的常见语法</h3><ol>
<li><p>事件的侦听与响应</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">start</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 响应事件，进行对应的操作</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// JavaScript</span></span><br><span class="line"><span class="built_in">document</span>.getElementById(<span class="string">'start'</span>).addEventListener(<span class="string">'click'</span>, start, <span class="literal">false</span>);</span><br><span class="line"><span class="comment">// jQuery</span></span><br><span class="line">$(<span class="string">"#start"</span>).on(<span class="string">'click'</span>, start);</span><br></pre></td></tr></table></figure>
</li>
<li><p>回调</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ajax</span></span><br><span class="line">$.ajax(&#123;</span><br><span class="line">    url: <span class="string">'https://baidu.com'</span>,</span><br><span class="line">    success: <span class="function"><span class="keyword">function</span> (<span class="params">res</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 回调函数</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 页面加载完毕之后回调</span></span><br><span class="line">$(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 回调函数</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id="2-2-异步回调的问题"><a href="#2-2-异步回调的问题" class="headerlink" title="2.2. 异步回调的问题"></a>2.2. 异步回调的问题</h3><ul>
<li>嵌套层次很深，难以维护<br>回调地狱<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">a(<span class="function"><span class="keyword">function</span> (<span class="params">arg1</span>) </span>&#123;</span><br><span class="line">    b(arg1,<span class="function"><span class="keyword">function</span> (<span class="params">arg2</span>) </span>&#123;</span><br><span class="line">        c(arg2,<span class="function"><span class="keyword">function</span> (<span class="params">arg3</span>) </span>&#123;</span><br><span class="line">            d(arg3,<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">                <span class="comment">// 回调地狱</span></span><br><span class="line">            &#125;)</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
</li>
</ul>
<ol start="2">
<li>无法正常的使用 return 和 throw<br>不能捕获异步函数的异常，因为异步函数是在不同的栈里面运行，没办法正常的使用 try catch 处理异步函数中的错误。</li>
<li>无法正常检索堆栈信息<br>每次回调都是在系统层面的一个新的堆栈</li>
<li>多个回调之间难以建立联系<br>一个回调一旦启动，我们再也没有办法对它操作</li>
</ol>
<h2 id="3-使用-Promise"><a href="#3-使用-Promise" class="headerlink" title="3. 使用 Promise"></a>3. 使用 Promise</h2><h3 id="3-1-两步执行"><a href="#3-1-两步执行" class="headerlink" title="3.1. 两步执行"></a>3.1. 两步执行</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 隔 2s 后输出 hello world，再隔 2s 后输出 hello promise</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'here we go'</span>);</span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        resolve(<span class="string">'hello'</span>);</span><br><span class="line">    &#125;,<span class="number">2000</span>);</span><br><span class="line">&#125;).then(<span class="function"><span class="params">value</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(value + <span class="string">' world'</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">       setTimeout(<span class="function"><span class="params">()</span>=&gt;</span>&#123;</span><br><span class="line">           resolve(<span class="string">'hello promise'</span>);</span><br><span class="line">       &#125;,<span class="number">2000</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;).then(<span class="function"><span class="params">value</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(value);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<h3 id="3-2-then"><a href="#3-2-then" class="headerlink" title="3.2. .then()"></a>3.2. .then()</h3><ul>
<li><code>.then()</code> 接收两个函数作为参数，分别代表 <code>fulfilled</code> 和 <code>rejected</code></li>
<li><code>.then()</code> 返回一个新的 Promise 实例，所以它可以链式调用</li>
<li>当前面的 Promise 状态改变时，<code>.then()</code> 根据其最终状态，选择特定的状态响应函数执行</li>
<li>状态响应函数可以返回新的 Promise，或者其它值；如果返回其它值，则会立刻执行下一级 <code>.then()</code></li>
<li>如果返回新的 Promise，那么下一级 <code>.then()</code> 会在新 Promise 状态改变之后执行</li>
</ul>
<h3 id="3-3-错误处理"><a href="#3-3-错误处理" class="headerlink" title="3.3. 错误处理"></a>3.3. 错误处理</h3><p>Promise 会自动捕获内部异常，并交给 rejected 响应函数处理。强烈建议在所有队列最后都加上 <code>.catch()</code>，以避免漏掉错误处理造成意想不到的问题。<br><strong>处理错误的两种方法</strong></p>
<ul>
<li>rejected(‘错误信息’)        .then(null, message =&gt; {});</li>
<li>throw new Error(‘错误信息’) .catch(message =&gt; {});</li>
</ul>
<p>推荐使用 catch 捕获异常，更加清晰好读，并且可以捕获前端的错误。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="string">'here we go'</span>);</span><br><span class="line"><span class="keyword">var</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve,reject</span>) =&gt;</span> &#123;</span><br><span class="line">    reject(<span class="string">'错误'</span>); <span class="comment">// throw new Error('错误');</span></span><br><span class="line">&#125;);</span><br><span class="line">promise.then(<span class="function"><span class="params">value</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(value);</span><br><span class="line">    <span class="keyword">return</span> value;</span><br><span class="line">&#125;).catch(<span class="function"><span class="params">value</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"catch:"</span> + value);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h2 id="4-Promise-常用函数"><a href="#4-Promise-常用函数" class="headerlink" title="4. Promise 常用函数"></a>4. Promise 常用函数</h2><h3 id="4-1-Promise-all"><a href="#4-1-Promise-all" class="headerlink" title="4.1. Promise.all()"></a>4.1. Promise.all()</h3><p>Promise.all 方法用于将多个 Promise 实例，包装成一个新的 Promise 实例。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.all([p1, p2, p3]);</span><br></pre></td></tr></table></figure></p>
<p>上面代码中，Promise.all 方法接受一个数组作为参数，p1、p2、p3 都是 Promise 对象的实例。（Promise.all 方法的参数不一定是数组，但是必须具有 iterator 接口，且返回的每个成员都是 Promise 实例。）<br>p 的状态由 p1、p2、p3 决定，分成两种情况:</p>
<ol>
<li>只有 p1、p2、p3 的状态都变成 fulfilled，p的状态才会变成 fulfilled，此时 p1、p2、p3 的返回值组成一个数组，传递给 p 的回调函数</li>
<li>只要 p1、p2、p3 之中有一个被 rejected，p的状态就变成 rejected，此时第一个被 reject 的实例的返回值，会传递给 p 的回调函数</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">console.log(&apos;here we go&apos;);</span><br><span class="line">Promise.all([1, 2, 3])</span><br><span class="line">    .then(all =&gt; &#123;</span><br><span class="line">        console.log(&quot;1: &quot; + all);</span><br><span class="line">        let promise1 = new Promise(resolve =&gt; &#123;</span><br><span class="line">            resolve(&apos;hello&apos;);</span><br><span class="line">        &#125;);</span><br><span class="line">        let promise2 = new Promise(resolve =&gt; &#123;</span><br><span class="line">            resolve(&apos;world&apos;);</span><br><span class="line">        &#125;);</span><br><span class="line">        return Promise.all([promise1, promise2]);</span><br><span class="line">    &#125;)</span><br><span class="line">    .then(all =&gt; &#123;</span><br><span class="line">        console.log(&quot;2: &quot; + all);</span><br><span class="line">        let promise3 = new Promise((resolve, reject) =&gt; &#123;</span><br><span class="line">            reject(&apos;错误-promise3&apos;);</span><br><span class="line">        &#125;);</span><br><span class="line">        let promise4 = new Promise((resolve, reject) =&gt; &#123;</span><br><span class="line">            reject(&apos;错误-promise4&apos;);</span><br><span class="line">        &#125;);</span><br><span class="line">        return Promise.all([promise3, promise4]);</span><br><span class="line">    &#125;).then(all =&gt; &#123;</span><br><span class="line">        console.log(all);</span><br><span class="line">    &#125;)</span><br><span class="line">    .catch(err =&gt; &#123;</span><br><span class="line">        console.log(&apos;catch: &apos; + err);</span><br><span class="line">    &#125;);</span><br></pre></td></tr></table></figure>
<h3 id="4-2-Promise-race"><a href="#4-2-Promise-race" class="headerlink" title="4.2. Promise.race()"></a>4.2. Promise.race()</h3><p>Promise.race 方法同样是将多个 Promise 实例，包装成一个新的 Promise 实例。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> p = <span class="built_in">Promise</span>.race([p1, p2, p3]);</span><br></pre></td></tr></table></figure></p>
<p>上面代码中，只要 p1、p2、p3 之中有一个实例率先改变状态，p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值，就传递给p的返回值。<br>如果 Promise.all 方法和 Promise.race 方法的参数，不是 Promise 实例，就会先调用下面讲到的 Promise.resolve 方法，将参数转为 Promise 实例，再进一步处理。</p>
<h3 id="4-3-Promise-resolve"><a href="#4-3-Promise-resolve" class="headerlink" title="4.3. Promise.resolve()"></a>4.3. Promise.resolve()</h3><p>有时需要将现有对象转为 Promise 对象，<code>Promise.resolve()</code> 方法就起到这个作用。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> jsPromise = <span class="built_in">Promise</span>.resolve($.ajax(<span class="string">'/whatever.json'</span>));</span><br></pre></td></tr></table></figure></p>
<p>上面代码将 jQuery 生成 deferred 对象，转为一个新的 ES6 的 Promise 对象。<br>如果 <code>Promise.resolve()</code> 方法的参数，不是具有 <code>then</code> 方法的对象（又称 <code>thenable 对象</code>），则返回一个新的 Promise 对象，且它的状态为 fulfilled。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> p = <span class="built_in">Promise</span>.resolve(<span class="string">'Hello'</span>);</span><br><span class="line"></span><br><span class="line">p.then(<span class="function"><span class="keyword">function</span> (<span class="params">s</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(s); <span class="comment">// Hello</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p>
<p>上面代码生成一个新的 Promise 对象的实例 p，它的状态为 fulfilled，所以回调函数会立即执行，<code>Promise.resolve()</code> 方法的参数就是回调函数的参数。<br>如果 <code>Promise.resolve()</code> 方法的参数是一个 Promise 对象的实例，则会被原封不动地返回。</p>
<h3 id="4-4-Promise-reject"><a href="#4-4-Promise-reject" class="headerlink" title="4.4. Promise.reject()"></a>4.4. Promise.reject()</h3><p><code>Promise.reject()</code> 方法也会返回一个新的 Promise 实例，该实例的状态为 rejected。<code>Promise.reject()</code> 方法的参数会被传递给实例的回调函数。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> p = <span class="built_in">Promise</span>.reject(<span class="string">'出错了'</span>);</span><br><span class="line"></span><br><span class="line">p.then(<span class="literal">null</span>, <span class="function"><span class="keyword">function</span> (<span class="params">s</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(s); <span class="comment">// 出错了</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p>
<p>上面代码生成一个 Promise 对象的实例 p，状态为 rejected，回调函数会立即执行。</p>
<h2 id="5-兼容性说明"><a href="#5-兼容性说明" class="headerlink" title="5. 兼容性说明"></a>5. 兼容性说明</h2><p>如果需要在 IE 中使用 Promise，方法有两种：</p>
<ul>
<li>只实现异步队列<br><a href="http://api.jquery.com/category/deferred-object/" target="_blank" rel="noopener">jQuery.defered</a></li>
<li>兼容所有平台<br><a href="https://github.com/petkaantonov/bluebird" target="_blank" rel="noopener">Bluebird</a><br><a href="https://github.com/taylorhakes/promise-polyfill" target="_blank" rel="noopener">Promise polyfill</a></li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/07/JS-常用设计模式/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/07/JS-常用设计模式/" itemprop="url">JavaScript 之常用设计模式</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-07T14:49:19+08:00">
                2018-06-07
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JavaScript/" itemprop="url" rel="index">
                    <span itemprop="name">JavaScript</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="1-单例模式"><a href="#1-单例模式" class="headerlink" title="1. 单例模式"></a>1. 单例模式</h2><h3 id="1-1-条件"><a href="#1-1-条件" class="headerlink" title="1.1. 条件"></a>1.1. 条件</h3><ul>
<li>确保只有一个实例</li>
<li>可以全局访问</li>
</ul>
<h3 id="1-2-适用"><a href="#1-2-适用" class="headerlink" title="1.2. 适用"></a>1.2. 适用</h3><ul>
<li>弹出框的实现</li>
<li>全局缓存</li>
</ul>
<h3 id="1-3-实现"><a href="#1-3-实现" class="headerlink" title="1.3. 实现"></a>1.3. 实现</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Singleton = <span class="function"><span class="keyword">function</span> (<span class="params">name</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">&#125;</span><br><span class="line">Singleton.prototype.getName = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line">&#125;</span><br><span class="line">Singleton.getInstance = <span class="function"><span class="keyword">function</span> (<span class="params">name</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="keyword">this</span>.instance) &#123;</span><br><span class="line">        <span class="keyword">this</span>.instance = <span class="keyword">new</span> Singleton(name);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.instance;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> a = Singleton.getInstance(<span class="string">'a'</span>);</span><br><span class="line"><span class="keyword">let</span> b = Singleton.getInstance(<span class="string">'b'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(a === b); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(b.getName()); <span class="comment">// a</span></span><br></pre></td></tr></table></figure>
<p>因为 Javascript 是无类的语言，而 JavaScript 中的全局对象符合单例模式的两个条件，很多时候我们把全局对象当成单例模式使用：<code>var obj ={}</code>。<br>下面是用闭包实现的一个单例模式：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> CreateDiv = (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> instance;</span><br><span class="line">    <span class="keyword">var</span> CreateDiv = <span class="function"><span class="keyword">function</span> (<span class="params">html</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (instance) &#123;</span><br><span class="line">            <span class="keyword">return</span> instance;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">this</span>.html = html;</span><br><span class="line">        <span class="keyword">this</span>.init();</span><br><span class="line">        <span class="keyword">return</span> instance = <span class="keyword">this</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">    CreateDiv.prototype.init = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> div = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>);</span><br><span class="line">        div.innerHTML = <span class="keyword">this</span>.html;</span><br><span class="line">        <span class="built_in">document</span>.body.appendChild(div);</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">return</span> CreateDiv;</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> CreateDiv(<span class="string">'sven1'</span>);</span><br><span class="line"><span class="keyword">var</span> b = <span class="keyword">new</span> CreateDiv(<span class="string">'sven2'</span>);</span><br><span class="line">alert(a === b); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p>
<p>高度抽象的单例模式代码，惰性单例的精髓：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 单例模式抽象，分离创建对象的函数和判断对象是否已经创建</span></span><br><span class="line"><span class="keyword">var</span> getSingle = <span class="function"><span class="keyword">function</span> (<span class="params">fn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> result;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> result || ( result = fn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>) );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h2 id="2-策略模式"><a href="#2-策略模式" class="headerlink" title="2. 策略模式"></a>2. 策略模式</h2><h3 id="2-1-定义"><a href="#2-1-定义" class="headerlink" title="2.1 定义"></a>2.1 定义</h3><p>根据不同参数可以命中不同的策略</p>
<h3 id="2-2-实现"><a href="#2-2-实现" class="headerlink" title="2.2. 实现"></a>2.2. 实现</h3><p>观察如下获取年终奖的 demo，根据不同的参数（level）获得不同策略方法(规则)，这是策略模式在 JS 比较经典的运用之一。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> strategy = &#123;</span><br><span class="line">    <span class="string">'S'</span>: <span class="function"><span class="keyword">function</span> (<span class="params">salary</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> salary * <span class="number">4</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">'A'</span>: <span class="function"><span class="keyword">function</span> (<span class="params">salary</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> salary * <span class="number">3</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">'B'</span>: <span class="function"><span class="keyword">function</span> (<span class="params">salary</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> salary * <span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> calculateBonus = <span class="function"><span class="keyword">function</span> (<span class="params">level, salary</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> strategy[level](salary);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">calculateBonus(<span class="string">'A'</span>, <span class="number">10000</span>); <span class="comment">// 30000</span></span><br></pre></td></tr></table></figure></p>
<p>在函数是一等公民的 JS 中，策略模式的使用常常隐藏在高阶函数中，稍微变换下上述 demo 的形式如下，可以发现我们平时已经在使用它了，恭喜我们又掌握了一种设计模式。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> S = <span class="function"><span class="keyword">function</span> (<span class="params">salary</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> salary * <span class="number">4</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> A = <span class="function"><span class="keyword">function</span> (<span class="params">salary</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> salary * <span class="number">3</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> B = <span class="function"><span class="keyword">function</span> (<span class="params">salary</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> salary * <span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> calculateBonus = <span class="function"><span class="keyword">function</span> (<span class="params">func, salary</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> func(salary);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">calculateBonus(A, <span class="number">10000</span>); <span class="comment">// 30000</span></span><br></pre></td></tr></table></figure></p>
<h3 id="2-3-优点"><a href="#2-3-优点" class="headerlink" title="2.3. 优点"></a>2.3. 优点</h3><ul>
<li>能减少大量 if 语句</li>
<li>复用性好</li>
</ul>
<h2 id="3-代理模式"><a href="#3-代理模式" class="headerlink" title="3. 代理模式"></a>3. 代理模式</h2><h3 id="3-1-情景"><a href="#3-1-情景" class="headerlink" title="3.1. 情景"></a>3.1. 情景</h3><p>情景：小明追女生 A</p>
<ul>
<li>非代理模式：小明 = 花 =&gt; 女生A</li>
<li>代理模式：小明 = 花 =&gt; 让女生A的好友B帮忙 = 花 =&gt; 女生A</li>
</ul>
<h3 id="3-2-特点"><a href="#3-2-特点" class="headerlink" title="3.2. 特点"></a>3.2. 特点</h3><p>代理对象和本体对象具有一致的接口，对使用者友好。<br>代理模式的种类有很多，在 JS 中最常用的为虚拟代理和缓存代理。</p>
<h3 id="3-3-实现"><a href="#3-3-实现" class="headerlink" title="3.3. 实现"></a>3.3. 实现</h3><p><strong>虚拟代理实现图片预加载</strong><br>下面这段代码运用代理模式来实现图片预加载，可以看到通过代理模式巧妙地将创建图片与预加载逻辑分离，并且在未来如果不需要预加载，只要改成请求本体代替请求代理对象就行。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myImage = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> imgNode = <span class="built_in">document</span>.createElement(<span class="string">'img'</span>);</span><br><span class="line">    <span class="built_in">document</span>.body.appendChild(imgNode);</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        setSrc: <span class="function"><span class="keyword">function</span>(<span class="params">src</span>) </span>&#123;</span><br><span class="line">            imgNode.src = src;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)();</span><br><span class="line"><span class="keyword">const</span> proxyImage = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> img = <span class="keyword">new</span> Image();</span><br><span class="line">    img.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; <span class="comment">// http 图片加载完毕后才会执行</span></span><br><span class="line">        myImage.setSrc(<span class="keyword">this</span>.src);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        setSrc: <span class="function"><span class="keyword">function</span>(<span class="params">src</span>) </span>&#123;</span><br><span class="line">            myImage.setSrc(<span class="string">'loading.jpg'</span>); <span class="comment">// 本地 loading 图片</span></span><br><span class="line">            img.src = src;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)()</span><br><span class="line">proxyImage.setSrc(<span class="string">'http://loaded.jpg'</span>);</span><br></pre></td></tr></table></figure></p>
<p><strong>缓存代理实现乘积计算</strong><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> mult = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> a = <span class="number">1</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, l; l = <span class="built_in">arguments</span>[i++];) &#123;</span><br><span class="line">        a = a * l</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> a</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> proxyMult = (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> cache = &#123;&#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">const</span> tag = <span class="built_in">Array</span>.prototype.join.call(<span class="built_in">arguments</span>, <span class="string">','</span>)</span><br><span class="line">        <span class="keyword">if</span> (cache[tag]) &#123;</span><br><span class="line">            <span class="keyword">return</span> cache[tag]</span><br><span class="line">        &#125;</span><br><span class="line">        cache[tag] = mult.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line">        <span class="keyword">return</span> cache[tag]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)()</span><br><span class="line"></span><br><span class="line">proxyMult(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>) <span class="comment">// 24</span></span><br></pre></td></tr></table></figure></p>
<h3 id="3-3-提示"><a href="#3-3-提示" class="headerlink" title="3.3. 提示"></a>3.3. 提示</h3><p>在开发时候不要先去猜测是否需要使用代理模式，如果发现直接使用某个对象不方便时，再来优化不迟。</p>
<h2 id="4-迭代器模式"><a href="#4-迭代器模式" class="headerlink" title="4. 迭代器模式"></a>4. 迭代器模式</h2><h3 id="4-1-定义"><a href="#4-1-定义" class="headerlink" title="4.1. 定义"></a>4.1. 定义</h3><p>能访问到聚合对象的顺序与元素</p>
<h3 id="4-2-实现"><a href="#4-2-实现" class="headerlink" title="4.2. 实现"></a>4.2. 实现</h3><p><strong>内部迭代器</strong><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">each</span>(<span class="params">arr, fn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">        fn(i, arr[i]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">each([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], <span class="function"><span class="keyword">function</span>(<span class="params">i, n</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(i); <span class="comment">// 0 1 2</span></span><br><span class="line">    <span class="built_in">console</span>.log(n); <span class="comment">// 1 2 3</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p>
<p>可以看出内部迭代器在调用的时候非常简单，使用者不用关心迭代器内部实现的细节，但这也是内部迭代器的缺点。比如要比较两数组是否相等，只能在其回调函数中作文章了，代码如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> compare = <span class="function"><span class="keyword">function</span>(<span class="params">arr1, arr2</span>) </span>&#123;</span><br><span class="line">  each(arr1, <span class="function"><span class="keyword">function</span>(<span class="params">i, n</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (arr2[i] !== n) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'两数组不等'</span>)</span><br><span class="line">      <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'两数组相等'</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr1 = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="keyword">const</span> arr2 = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">compare(arr1, arr2) <span class="comment">// 两数组相等</span></span><br></pre></td></tr></table></figure></p>
<p><strong>外部迭代器</strong><br>相较于内部迭代器，外部迭代器将遍历的权利转移到外部，因此在调用的时候拥有了更多的自由性，不过缺点是调用方式较复杂。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> iterator = <span class="function"><span class="keyword">function</span> (<span class="params">arr</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> current = <span class="number">0</span></span><br><span class="line">    <span class="keyword">const</span> next = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        current = current + <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> done = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> current &gt;= arr.length</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> value = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> arr[current]</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        next,</span><br><span class="line">        done,</span><br><span class="line">        value,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr1 = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="keyword">const</span> arr2 = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="keyword">const</span> iterator1 = iterator(arr1)</span><br><span class="line"><span class="keyword">const</span> iterator2 = iterator(arr2)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> compare = <span class="function"><span class="keyword">function</span> (<span class="params">iterator1, iterator2</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (!iterator1.done() &amp;&amp; !iterator2.done()) &#123;</span><br><span class="line">        <span class="keyword">if</span> (iterator1.value() !== iterator2.value()) &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'两数组不等'</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        &#125;</span><br><span class="line">        iterator1.next() <span class="comment">// 外部迭代器将遍历的权利转移到外部</span></span><br><span class="line">        iterator2.next()</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'两数组相等'</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">compare(iterator1, iterator2)</span><br></pre></td></tr></table></figure></p>
<h2 id="5-发布订阅模式"><a href="#5-发布订阅模式" class="headerlink" title="5. 发布订阅模式"></a>5. 发布订阅模式</h2><h3 id="5-1-说明"><a href="#5-1-说明" class="headerlink" title="5.1. 说明"></a>5.1. 说明</h3><p>事件发布/订阅模式 (PubSub) 在异步编程中帮助我们完成更松的解耦，甚至在 MVC、MVVC 的架构中以及设计模式中也少不了发布-订阅模式的参与。<br>优点：在异步编程中实现更深的解耦<br>缺点：如果过多的使用发布订阅模式，会增加维护的难度</p>
<h3 id="5-2-实现"><a href="#5-2-实现" class="headerlink" title="5.2. 实现"></a>5.2. 实现</h3><p><strong>实现一个发布订阅模式</strong><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Event = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.obj = &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Event.prototype.on = <span class="function"><span class="keyword">function</span> (<span class="params">eventType, fn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="keyword">this</span>.obj[eventType]) &#123;</span><br><span class="line">        <span class="keyword">this</span>.obj[eventType] = []</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">this</span>.obj[eventType].push(fn)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Event.prototype.emit = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> eventType = <span class="built_in">Array</span>.prototype.shift.call(<span class="built_in">arguments</span>)</span><br><span class="line">    <span class="keyword">var</span> arr = <span class="keyword">this</span>.obj[eventType]</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">        arr[i].apply(arr[i], <span class="built_in">arguments</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> ev = <span class="keyword">new</span> Event()</span><br><span class="line"></span><br><span class="line">ev.on(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">a</span>) </span>&#123; <span class="comment">// 订阅函数</span></span><br><span class="line">    <span class="built_in">console</span>.log(a) <span class="comment">// 1</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">ev.emit(<span class="string">'click'</span>, <span class="number">1</span>)          <span class="comment">// 发布函数</span></span><br></pre></td></tr></table></figure></p>
<p><strong>订阅函数逻辑一定要优先于发布函数吗</strong><br>考虑以下场景：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$.ajax(<span class="string">''</span>, () =&gt; &#123;</span><br><span class="line">  <span class="comment">// 异步订阅函数逻辑</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在其他地方执行发布函数，此时并不能保证执行发布函数的时候，订阅函数已经执行</span></span><br></pre></td></tr></table></figure></p>
<p>我们需要实现这样的逻辑：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> ev = <span class="keyword">new</span> Event()</span><br><span class="line">ev.emit(<span class="string">'click'</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">ev.on(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">a</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(a) <span class="comment">// 1</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p>
<p>目标明确后，来着手实现它：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Event = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.obj = &#123;&#125;</span><br><span class="line">  <span class="keyword">this</span>.cacheList = []</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Event.prototype.on = <span class="function"><span class="keyword">function</span>(<span class="params">eventType, fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (!<span class="keyword">this</span>.obj[eventType]) &#123;</span><br><span class="line">    <span class="keyword">this</span>.obj[eventType] = []</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">this</span>.obj[eventType].push(fn)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="keyword">this</span>.cacheList.length; i++) &#123;</span><br><span class="line">    <span class="keyword">this</span>.cacheList[i]()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Event.prototype.emit = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> arg = <span class="built_in">arguments</span></span><br><span class="line">  <span class="keyword">const</span> that = <span class="keyword">this</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">cache</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> eventType = <span class="built_in">Array</span>.prototype.shift.call(arg)</span><br><span class="line">    <span class="keyword">var</span> arr = that.obj[eventType]</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">      arr[i].apply(arr[i], arg)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">this</span>.cacheList.push(cache)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>以上代码实现思路就是把原本在 emit 里触发的函数存到 cacheList，再转交到 on 中触发。从而实现了发布函数先于订阅函数执行。</p>
<h2 id="6-命令模式"><a href="#6-命令模式" class="headerlink" title="6. 命令模式"></a>6. 命令模式</h2><p>命令模式与策略模式有些类似，在 JavaScript 中它们都是隐式的。<br>重要性：较低</p>
<h3 id="6-1-实现"><a href="#6-1-实现" class="headerlink" title="6.1. 实现"></a>6.1. 实现</h3><p>命令模式在 JavaScript 中也比较简单，下面代码中对按钮和命令进行了抽离，因此可以复杂项目中可以使用命令模式将界面的代码和功能的代码交付给不同的人去写。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> setCommand = <span class="function"><span class="keyword">function</span>(<span class="params">button, command</span>) </span>&#123;</span><br><span class="line">  button.onClick = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    command.excute()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// --------------------  上面的界面逻辑由A完成，下面的由B完成</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> menu = &#123;</span><br><span class="line">  updateMenu: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'更新菜单'</span>)</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> UpdateCommand = <span class="function"><span class="keyword">function</span>(<span class="params">receive</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    excute: receive.updateMenu,</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> updateCommand = UpdateCommand(menu) <span class="comment">// 创建命令</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> button1 = <span class="built_in">document</span>.getElementById(<span class="string">'button1'</span>)</span><br><span class="line">setCommand(button1, updateCommand)</span><br></pre></td></tr></table></figure></p>
<h2 id="11-中介者模式"><a href="#11-中介者模式" class="headerlink" title="11. 中介者模式"></a>11. 中介者模式</h2><h3 id="11-1-作用"><a href="#11-1-作用" class="headerlink" title="11.1 作用"></a>11.1 作用</h3><p>对象和对象之间借助第三方中介者进行通信。</p>
<h3 id="11-2-实现"><a href="#11-2-实现" class="headerlink" title="11.2. 实现"></a>11.2. 实现</h3><p>一场测试结束后，公布结果：告知解答出题目的人挑战成功，否则挑战失败。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> player = <span class="function"><span class="keyword">function</span> (<span class="params">name</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name</span><br><span class="line">    playerMiddle.add(name)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">player.prototype.win = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    playerMiddle.win(<span class="keyword">this</span>.name)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">player.prototype.lose = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    playerMiddle.lose(<span class="keyword">this</span>.name)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> playerMiddle = (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; <span class="comment">// 将就用下这个 demo，这个函数当成中介者</span></span><br><span class="line">    <span class="keyword">const</span> players = []</span><br><span class="line">    <span class="keyword">const</span> winArr = []</span><br><span class="line">    <span class="keyword">const</span> loseArr = []</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        add: <span class="function"><span class="keyword">function</span> (<span class="params">name</span>) </span>&#123;</span><br><span class="line">            players.push(name)</span><br><span class="line">        &#125;,</span><br><span class="line">        win: <span class="function"><span class="keyword">function</span> (<span class="params">name</span>) </span>&#123;</span><br><span class="line">            winArr.push(name)</span><br><span class="line">            <span class="keyword">if</span> (winArr.length + loseArr.length === players.length) &#123;</span><br><span class="line">                <span class="keyword">this</span>.show()</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        lose: <span class="function"><span class="keyword">function</span> (<span class="params">name</span>) </span>&#123;</span><br><span class="line">            loseArr.push(name)</span><br><span class="line">            <span class="keyword">if</span> (winArr.length + loseArr.length === players.length) &#123;</span><br><span class="line">                <span class="keyword">this</span>.show()</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        show: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">let</span> winner <span class="keyword">of</span> winArr) &#123;</span><br><span class="line">                <span class="built_in">console</span>.log(winner + <span class="string">'挑战成功;'</span>)</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">let</span> loser <span class="keyword">of</span> loseArr) &#123;</span><br><span class="line">                <span class="built_in">console</span>.log(loser + <span class="string">'挑战失败;'</span>)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;())</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> a = <span class="keyword">new</span> player(<span class="string">'A 选手'</span>)</span><br><span class="line"><span class="keyword">const</span> b = <span class="keyword">new</span> player(<span class="string">'B 选手'</span>)</span><br><span class="line"><span class="keyword">const</span> c = <span class="keyword">new</span> player(<span class="string">'C 选手'</span>)</span><br><span class="line"></span><br><span class="line">a.win()</span><br><span class="line">b.win()</span><br><span class="line">c.lose()</span><br><span class="line"></span><br><span class="line"><span class="comment">// A 选手挑战成功;</span></span><br><span class="line"><span class="comment">// B 选手挑战成功;</span></span><br><span class="line"><span class="comment">// C 选手挑战失败;</span></span><br></pre></td></tr></table></figure></p>
<p>在这段代码中 A、B、C 之间没有直接发生关系，而是通过另外的 playerMiddle 对象建立链接，姑且将之当成是中介者模式了。</p>
<h2 id="12-装饰者模式"><a href="#12-装饰者模式" class="headerlink" title="12. 装饰者模式"></a>12. 装饰者模式</h2><h3 id="12-1-作用"><a href="#12-1-作用" class="headerlink" title="12.1. 作用"></a>12.1. 作用</h3><p>动态地给函数赋能</p>
<h3 id="12-2-实现"><a href="#12-2-实现" class="headerlink" title="12.2. 实现"></a>12.2. 实现</h3><p>生活中的例子：天气冷了，就添加衣服来保暖；天气热了，就将外套脱下；这个例子很形象地含盖了装饰器的神韵，随着天气的冷暖变化，衣服可以动态的穿上脱下。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> wear = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'穿上第一件衣服'</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> _wear1 = wear</span><br><span class="line"></span><br><span class="line">wear = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    _wear1()</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'穿上第二件衣服'</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> _wear2 = wear</span><br><span class="line"></span><br><span class="line">wear = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    _wear2()</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'穿上第三件衣服'</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">wear()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 穿上第一件衣服</span></span><br><span class="line"><span class="comment">// 穿上第二件衣服</span></span><br><span class="line"><span class="comment">// 穿上第三件衣服</span></span><br></pre></td></tr></table></figure></p>
<p>这种方式有以下缺点：1：临时变量会变得越来越多；2：this 指向有时会出错<br><strong>AOP 装饰函数</strong><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 前置代码</span></span><br><span class="line"><span class="built_in">Function</span>.prototype.before = <span class="function"><span class="keyword">function</span> (<span class="params">fn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> self = <span class="keyword">this</span></span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        fn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line">        <span class="keyword">return</span> self.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 后置代码</span></span><br><span class="line"><span class="built_in">Function</span>.prototype.after = <span class="function"><span class="keyword">function</span> (<span class="params">fn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> self = <span class="keyword">this</span></span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        self.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line">        <span class="keyword">return</span> fn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>用后置代码来实验下上面穿衣服的 demo，<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> wear1 = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'穿上第一件衣服'</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> wear2 = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'穿上第二件衣服'</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> wear3 = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'穿上第三件衣服'</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> wear = wear1.after(wear2).after(wear3)</span><br><span class="line">wear()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 穿上第一件衣服</span></span><br><span class="line"><span class="comment">// 穿上第二件衣服</span></span><br><span class="line"><span class="comment">// 穿上第三件衣服</span></span><br></pre></td></tr></table></figure></p>
<p>但这样子有时会污染原生函数，可以做点通变<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> after = <span class="function"><span class="keyword">function</span> (<span class="params">fn, afterFn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        fn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line">        afterFn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> wear = after(after(wear1, wear2), wear3)</span><br><span class="line">wear()</span><br></pre></td></tr></table></figure></p>
<h2 id="13-状态模式"><a href="#13-状态模式" class="headerlink" title="13. 状态模式"></a>13. 状态模式</h2><h3 id="13-1-定义"><a href="#13-1-定义" class="headerlink" title="13.1. 定义"></a>13.1. 定义</h3><p>将事物内部的每个状态分别封装成类，内部状态改变会产生不同行为。</p>
<ul>
<li>优点：用对象代替字符串记录当前状态，状态易维护</li>
<li>缺点：需编写大量状态类对象</li>
</ul>
<h3 id="13-2-实现"><a href="#13-2-实现" class="headerlink" title="13.2. 实现"></a>13.2. 实现</h3><p>某某牌电灯，按一下按钮打开弱光，按两下按钮打开强光，按三下按钮关闭灯光。<br><strong>面向对象实现的状态模式</strong><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 将状态封装成不同类</span></span><br><span class="line"><span class="keyword">const</span> weakLight = <span class="function"><span class="keyword">function</span> (<span class="params">light</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.light = light</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">weakLight.prototype.press = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'打开强光'</span>)</span><br><span class="line">    <span class="keyword">this</span>.light.setState(<span class="keyword">this</span>.light.strongLight)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> strongLight = <span class="function"><span class="keyword">function</span> (<span class="params">light</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.light = light</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">strongLight.prototype.press = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'关灯'</span>)</span><br><span class="line">    <span class="keyword">this</span>.light.setState(<span class="keyword">this</span>.light.offLight)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> offLight = <span class="function"><span class="keyword">function</span> (<span class="params">light</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.light = light</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">offLight.prototype.press = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'打开弱光'</span>)</span><br><span class="line">    <span class="keyword">this</span>.light.setState(<span class="keyword">this</span>.light.weakLight)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Light = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.weakLight = <span class="keyword">new</span> weakLight(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.strongLight = <span class="keyword">new</span> strongLight(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.offLight = <span class="keyword">new</span> offLight(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.currentState = <span class="keyword">this</span>.offLight          <span class="comment">// 初始状态</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Light.prototype.init = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> btn = <span class="built_in">document</span>.createElement(<span class="string">'button'</span>)</span><br><span class="line">    btn.innerHTML = <span class="string">'按钮'</span></span><br><span class="line">    <span class="built_in">document</span>.body.append(btn)</span><br><span class="line">    <span class="keyword">const</span> self = <span class="keyword">this</span></span><br><span class="line">    btn.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        self.currentState.press()</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Light.prototype.setState = <span class="function"><span class="keyword">function</span> (<span class="params">state</span>) </span>&#123; <span class="comment">// 改变当前状态</span></span><br><span class="line">    <span class="keyword">this</span>.currentState = state</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> light = <span class="keyword">new</span> Light()</span><br><span class="line">light.init()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 打开弱光</span></span><br><span class="line"><span class="comment">// 打开强光</span></span><br><span class="line"><span class="comment">// 关灯</span></span><br></pre></td></tr></table></figure></p>
<p><strong>非面向对象实现的状态模式</strong><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = &#123;</span><br><span class="line">    <span class="string">'weakLight'</span>: &#123;</span><br><span class="line">        press: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'打开强光'</span>)</span><br><span class="line">            <span class="keyword">this</span>.currentState = obj.strongLight</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">'strongLight'</span>: &#123;</span><br><span class="line">        press: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'关灯'</span>)</span><br><span class="line">            <span class="keyword">this</span>.currentState = obj.offLight</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">'offLight'</span>: &#123;</span><br><span class="line">        press: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'打开弱光'</span>)</span><br><span class="line">            <span class="keyword">this</span>.currentState = obj.weakLight</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Light = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.currentState = obj.offLight</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Light.prototype.init = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> btn = <span class="built_in">document</span>.createElement(<span class="string">'button'</span>)</span><br><span class="line">    btn.innerHTML = <span class="string">'按钮'</span></span><br><span class="line">    <span class="built_in">document</span>.body.append(btn)</span><br><span class="line">    <span class="keyword">const</span> self = <span class="keyword">this</span></span><br><span class="line">    btn.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        self.currentState.press.call(self) <span class="comment">// 通过 call 完成委托</span></span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> light = <span class="keyword">new</span> Light()</span><br><span class="line">light.init()</span><br></pre></td></tr></table></figure></p>
<h2 id="14-适配者模式"><a href="#14-适配者模式" class="headerlink" title="14. 适配者模式"></a>14. 适配者模式</h2><h3 id="14-1-作用"><a href="#14-1-作用" class="headerlink" title="14.1. 作用"></a>14.1. 作用</h3><p>主要用于解决两个接口之间不匹配的问题。</p>
<h3 id="14-2-实现"><a href="#14-2-实现" class="headerlink" title="14.2. 实现"></a>14.2. 实现</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> zheJiangCityOld = (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> [</span><br><span class="line">        &#123;</span><br><span class="line">            name: <span class="string">'hangzhou'</span>,</span><br><span class="line">            id: <span class="number">11</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">            name: <span class="string">'jinhua'</span>,</span><br><span class="line">            id: <span class="number">12</span>,</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新接口期望的类型 &#123; hangzhou: 11,jinhua: 12 &#125;</span></span><br><span class="line"><span class="keyword">const</span> adaptor = (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> obj = &#123;&#125;;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> city <span class="keyword">of</span> zheJiangCityOld) &#123;</span><br><span class="line">        obj[city.name] = city.id;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> obj;</span><br><span class="line">&#125;)();</span><br><span class="line"><span class="built_in">console</span>.log(adaptor); <span class="comment">// &#123; hangzhou: 11,jinhua: 12 &#125;</span></span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/06/JS-原型链/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/06/JS-原型链/" itemprop="url">JavaScript 之原型到原型链™</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-06T15:34:03+08:00">
                2018-06-06
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JavaScript/" itemprop="url" rel="index">
                    <span itemprop="name">JavaScript</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="1-原型简介"><a href="#1-原型简介" class="headerlink" title="1. 原型简介"></a>1. 原型简介</h2><h3 id="1-1-什么是原型"><a href="#1-1-什么是原型" class="headerlink" title="1.1. 什么是原型"></a>1.1. 什么是原型</h3><p>原型是 Javascript 中的继承的基础，JavaScript 的继承就是基于原型的继承。</p>
<h3 id="1-2-prototype-和-proto-的区别"><a href="#1-2-prototype-和-proto-的区别" class="headerlink" title="1.2. prototype 和 __proto__ 的区别"></a>1.2. prototype 和 __proto__ 的区别</h3><ul>
<li>prototype<br>prototype 是函数才有的属性</li>
<li>__proto__<br>__proto__ 是每个对象都有的属性<br>但 __proto__ 不是一个规范属性，只有部分浏览器实现了此属性，对应的标准属性是 [[Prototype]]</li>
</ul>
<h2 id="2-实例介绍"><a href="#2-实例介绍" class="headerlink" title="2. 实例介绍"></a>2. 实例介绍</h2><h3 id="2-1-构造函数创建对象"><a href="#2-1-构造函数创建对象" class="headerlink" title="2.1. 构造函数创建对象"></a>2.1. 构造函数创建对象</h3><p>使用构造函数创建一个对象：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> person = <span class="keyword">new</span> Person();</span><br><span class="line">person.name = <span class="string">'xiguapi'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(person.name); <span class="comment">// xiguapi</span></span><br></pre></td></tr></table></figure></p>
<p>在这个例子中，Person 就是一个构造函数，我们使用 new 创建了一个实例对象 person。</p>
<h3 id="2-2-prototype"><a href="#2-2-prototype" class="headerlink" title="2.2. prototype"></a>2.2. prototype</h3><p>每个函数都有一个 prototype 属性，比如：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line">Person.prototype.name = <span class="string">"xiguapi"</span>;</span><br><span class="line"><span class="keyword">var</span> person1 = <span class="keyword">new</span> Person();</span><br><span class="line"><span class="keyword">var</span> person2 = <span class="keyword">new</span> Person();</span><br><span class="line"><span class="built_in">console</span>.log(person1.name); <span class="comment">// xiguapi</span></span><br><span class="line"><span class="built_in">console</span>.log(person2.name); <span class="comment">// xiguapi</span></span><br></pre></td></tr></table></figure></p>
<p>其实，函数的 prototype 属性指向了一个对象，这个对象正是调用该构造函数而创建的实例的原型，也就是这个例子中的 person1 和 person2 的原型。<br>那什么是原型呢？你可以这样理解：每一个 JavaScript 对象( null，undefined 除外)在创建的时候就会与之关联另一个对象，这个对象就是我们所说的原型，每一个对象都会从原型”继承”属性。<br>用一张图表示构造函数和实例原型之间的关系：<br><img src="http://xiezixi.oschina.io/blog/images/prototype1.png" alt="prototype"><br>在这张图中我们用 Person.prototype 表示实例原型。<br>那么我们该怎么表示实例与实例原型，也就是 person 和 Person.prototype 之间的关系呢，这时候我们就要讲到第二个属性：</p>
<h3 id="2-3-proto"><a href="#2-3-proto" class="headerlink" title="2.3. __proto__"></a>2.3. __proto__</h3><p>每一个JavaScript对象(除了 null，undefined 外 )都具有的一个属性，叫 __proto__ ，这个属性会指向该对象的原型。<br>为了证明这一点,我们可以在火狐或者谷歌中输入：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> person = <span class="keyword">new</span> Person();</span><br><span class="line"><span class="built_in">console</span>.log(person.__proto__ === Person.prototype); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p>
<p>于是我们更新下关系图：<br><img src="http://xiezixi.oschina.io/blog/images/prototype2.png" alt="prototype"></p>
<h3 id="2-4-constructor"><a href="#2-4-constructor" class="headerlink" title="2.4. constructor"></a>2.4. constructor</h3><p>因为一个构造函数可以生成多个实例，这就要讲到第三个属性：constructor，每个原型都有一个 constructor 属性指向关联的构造函数。<br>为了验证这一点，我们可以尝试：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(Person === Person.prototype.constructor); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p>
<p>所以再更新下关系图：<br><img src="http://xiezixi.oschina.io/blog/images/prototype3.png" alt="prototype"><br>综上我们已经得出：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> person = <span class="keyword">new</span> Person();</span><br><span class="line"><span class="built_in">console</span>.log(person.__proto__ === Person.prototype); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(Person.prototype.constructor === Person); <span class="comment">// true</span></span><br><span class="line"><span class="comment">// ES5 获取对象原型</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.getPrototypeOf(person) === Person.prototype); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p>
<h3 id="2-5-实例与原型"><a href="#2-5-实例与原型" class="headerlink" title="2.5. 实例与原型"></a>2.5. 实例与原型</h3><p>当读取实例的属性时，如果找不到，就会查找与对象关联的原型中的属性，如果还查不到，就去找原型的原型，一直找到最顶层为止。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Person.prototype.name = <span class="string">'Kevin'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = <span class="keyword">new</span> Person();</span><br><span class="line"></span><br><span class="line">person.name = <span class="string">'Daisy'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(person.name) <span class="comment">// Daisy</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">delete</span> person.name;</span><br><span class="line"><span class="built_in">console</span>.log(person.name) <span class="comment">// Kevin</span></span><br></pre></td></tr></table></figure></p>
<p>在这个例子中，我们给实例对象 person 添加了 name 属性，当我们打印 person.name 的时候，结果自然为 Daisy。<br>但是当我们删除了 person 的 name 属性时，读取 person.name，从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.<strong>proto</strong> ，也就是 Person.prototype中查找，幸运的是我们找到了 name 属性，结果为 Kevin。</p>
<h3 id="2-6-原型的原型"><a href="#2-6-原型的原型" class="headerlink" title="2.6. 原型的原型"></a>2.6. 原型的原型</h3><p>我们在 JavaScript 遇到的每个对象，实际上都是从 Object.prototype 对象克隆而来的，Object.prototype 对象就是它们的原型。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(Person.prototype.__proto__ === <span class="built_in">Object</span>.prototype); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p>
<p>其实原型对象就是通过 Object 构造函数生成的，结合之前所讲，实例的 __proto__ 指向构造函数的 prototype ，所以我们再更新下关系图：<br><img src="http://xiezixi.oschina.io/blog/images/prototype4.png" alt="prototype"></p>
<h3 id="2-7-原型链"><a href="#2-7-原型链" class="headerlink" title="2.7. 原型链"></a>2.7. 原型链</h3><p>Object.prototype 的原型是 null。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.prototype.__proto__ === <span class="literal">null</span>) <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p>
<p>所以 Object.prototype.__proto__  的值为 null 跟 Object.prototype 没有原型，其实表达了一个意思。<br>所以查找属性的时候查到 Object.prototype 就可以停止查找了。<br>最后一张关系图也可以更新为：<br><img src="http://xiezixi.oschina.io/blog/images/prototype5.png" alt="prototype"></p>
<h2 id="3-注意点"><a href="#3-注意点" class="headerlink" title="3. 注意点"></a>3. 注意点</h2><h3 id="3-1-constructor"><a href="#3-1-constructor" class="headerlink" title="3.1. constructor"></a>3.1. constructor</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> person = <span class="keyword">new</span> Person();</span><br><span class="line"><span class="built_in">console</span>.log(person.constructor === Person); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<p>当获取 person.constructor 时，其实 person 中并没有 constructor 属性,当不能读取到 constructor 属性时，会从 person 的原型也就是 Person.prototype 中读取，正好原型中有该属性，所以：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">person.constructor === Person.prototype.constructor</span><br></pre></td></tr></table></figure></p>
<h3 id="3-2-proto"><a href="#3-2-proto" class="headerlink" title="3.2. __proto__"></a>3.2. __proto__</h3><p>其次是 __proto__ ，绝大部分浏览器都支持这个非标准的方法访问原型，然而它并不存在于 Person.prototype 中，实际上，它是来自于 Object.prototype ，与其说是一个属性，不如说是一个 getter/setter，当使用 obj.__proto__ 时，可以理解成返回了 <code>Object.getPrototypeOf(obj)</code>。</p>
<h3 id="3-3-继承"><a href="#3-3-继承" class="headerlink" title="3.3. 继承"></a>3.3. 继承</h3><p>继承意味着复制操作，然而 JavaScript 默认并不会复制对象的属性，相反，JavaScript 只是在两个对象之间创建一个关联，这样，一个对象就可以通过委托访问另一个对象的属性和函数，所以与其叫继承，委托的说法反而更准确些。<br>所有对象最终的基类都是 Object，因此 Object.prototype 是所有对象最上层的原型对象。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/04/ES6-箭头函数/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/04/ES6-箭头函数/" itemprop="url">ECMAScript6 之箭头函数™</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-04T15:42:46+08:00">
                2018-06-04
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/ECMAScript6/" itemprop="url" rel="index">
                    <span itemprop="name">ECMAScript6</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="1-什么是箭头函数"><a href="#1-什么是箭头函数" class="headerlink" title="1. 什么是箭头函数"></a>1. 什么是箭头函数</h2><h3 id="1-1-箭头函数简介"><a href="#1-1-箭头函数简介" class="headerlink" title="1.1. 箭头函数简介"></a>1.1. 箭头函数简介</h3><p>箭头函数的语法非常简单 <code>(parameters) =&gt; { statements }</code>，箭头函数只能用赋值式写法，不能用声明式写法。看一下最简单的箭头函数表示法：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> sayHello = <span class="function"><span class="params">()</span> =&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">'Hello World'</span>); &#125;</span><br><span class="line">sayHello(); <span class="comment">// Hello World</span></span><br></pre></td></tr></table></figure></p>
<h3 id="1-2-箭头函数的特点"><a href="#1-2-箭头函数的特点" class="headerlink" title="1.2. 箭头函数的特点"></a>1.2. 箭头函数的特点</h3><p>和普通函数相比，箭头函数主要就是以下三个方面的特点：</p>
<ul>
<li>更简化的代码语法</li>
<li>没有局部 this 的绑定</li>
<li>不绑定 arguments</li>
</ul>
<h2 id="2-更简化的代码语法"><a href="#2-更简化的代码语法" class="headerlink" title="2. 更简化的代码语法"></a>2. 更简化的代码语法</h2><h3 id="2-1-一般写法"><a href="#2-1-一般写法" class="headerlink" title="2.1. 一般写法"></a>2.1. 一般写法</h3><p><code>(parameters) =&gt; { statements }</code><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 求两个数的和并在控制台输出</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">let</span> sum = <span class="function">(<span class="params">a, b</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(a + b);</span><br><span class="line">  <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="keyword">var</span> sum = <span class="function"><span class="keyword">function</span>(<span class="params">a, b</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(a + b);</span><br><span class="line">  <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h3 id="2-2-省略-的情况"><a href="#2-2-省略-的情况" class="headerlink" title="2.2. 省略 () 的情况"></a>2.2. 省略 () 的情况</h3><p>如果只有一个参数，可以省略<code>()</code>括号。<br><code>parameters =&gt; { statements }</code><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 求一个数的两倍，并输出</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">let</span> double = <span class="function"><span class="params">num</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(num * <span class="number">2</span>);</span><br><span class="line">  <span class="keyword">return</span> num * <span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="keyword">var</span> double = <span class="function"><span class="keyword">function</span>(<span class="params">num</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(num * <span class="number">2</span>);</span><br><span class="line">  <span class="keyword">return</span> num * <span class="number">2</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h3 id="2-3-省略-的情况"><a href="#2-3-省略-的情况" class="headerlink" title="2.3. 省略 {} 的情况"></a>2.3. 省略 {} 的情况</h3><p>如果返回值仅仅只有一个表达式（expression）, 还可以省略大括号<code>{}</code>。<br><code>parameters =&gt; statements</code><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 求一个数的两倍</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">let</span> double = <span class="function"><span class="params">num</span> =&gt;</span> num * <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="keyword">var</span> double = <span class="function"><span class="keyword">function</span>(<span class="params">num</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> num * <span class="number">2</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h3 id="2-4-特殊情况"><a href="#2-4-特殊情况" class="headerlink" title="2.4. 特殊情况"></a>2.4. 特殊情况</h3><p>如果箭头函数直接返回一个对象，必须在对象外面加上括号。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">let</span> returnObj = <span class="function"><span class="params">()</span> =&gt;</span> (&#123;<span class="attr">name</span>: <span class="string">"xiguapi"</span>, <span class="attr">age</span>: <span class="number">23</span>&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="keyword">var</span> returnObj = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        name: <span class="string">"xiguapi"</span>,</span><br><span class="line">        age: <span class="number">23</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h2 id="3-没有局部-this-的绑定"><a href="#3-没有局部-this-的绑定" class="headerlink" title="3. 没有局部 this 的绑定"></a>3. 没有局部 this 的绑定</h2><p>和一般的函数不同，箭头函数不会绑定 <code>this</code>。 或则说箭头函数不会改变 <code>this</code> 本来的绑定。<br>我们用一个例子来说明：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Counter</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.num = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> Counter(); <span class="comment">// a.num 0</span></span><br></pre></td></tr></table></figure></p>
<p>因为使用了关键字 <code>new</code> 构造，<code>Count()</code> 函数中的 <code>this</code>绑定到一个新的对象，并且赋值给 <code>a</code>。通过 <code>console.log</code> 打印 <code>a.num</code>，会输出 0。</p>
<p>如果我们想每过一秒将 <code>a.num</code> 的值加 1，该如何实现呢？可以使用 <code>setInterval()</code> 函数。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Counter</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.num = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">this</span>.timer = setInterval(<span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.num++;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.num);</span><br><span class="line">  &#125;, <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = <span class="keyword">new</span> Counter();</span><br><span class="line"><span class="comment">// NaN</span></span><br><span class="line"><span class="comment">// NaN</span></span><br><span class="line"><span class="comment">// NaN</span></span><br><span class="line"><span class="comment">// ...</span></span><br></pre></td></tr></table></figure></p>
<p>首先函数 <code>setInterval</code> 没有被某个声明的对象调用，也没有使用 <code>new</code> 关键字，再之没有使用 <code>bind</code>, <code>call</code> 和 <code>apply</code>。<code>setInterval</code> 只是一个普通的函数。实际上 <code>setInterval</code> 里面的 <code>this</code> 绑定到全局对象 <code>window</code> 上了。</p>
<p>使用箭头函数！使用箭头函数就不会导致 <code>this</code> 被绑定到全局对象。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">function Counter() &#123;</span><br><span class="line">  this.num = 0;</span><br><span class="line">  this.timer = setInterval(() =&gt; &#123;</span><br><span class="line">    this.num++;</span><br><span class="line">    console.log(this.num);</span><br><span class="line">  &#125;, 1000);</span><br><span class="line">&#125;</span><br><span class="line">var b = new Counter();</span><br><span class="line">// 1</span><br><span class="line">// 2</span><br><span class="line">// 3</span><br><span class="line">// ...</span><br></pre></td></tr></table></figure></p>
<p>通过 <code>Counter</code> 构造函数绑定的 <code>this</code> 将会被保留。在 <code>setInterval</code> 函数中，<code>this</code> 依然指向我们新创建的 <code>b</code> 对象。</p>
<h2 id="3-不绑定-arguments"><a href="#3-不绑定-arguments" class="headerlink" title="3. 不绑定 arguments"></a>3. 不绑定 arguments</h2><p>箭头函数还有一个比较有特点的地方就是其不绑定 arguments,即如果你在箭头函数中使用 arguments 参数不能得到想要的内容。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arrowfunc = <span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="built_in">arguments</span>.length)</span><br><span class="line">arrowfunc()</span><br><span class="line"><span class="comment">// arguments is not defined</span></span><br></pre></td></tr></table></figure></p>
<h2 id="4-注意点"><a href="#4-注意点" class="headerlink" title="4. 注意点"></a>4. 注意点</h2><ul>
<li>箭头函数适合于无复杂逻辑或者无副作用的纯函数场景下，例如：用在 map、reduce、filter 的回调函数定义中</li>
<li>箭头函数的亮点是简洁，但在有多层函数嵌套的情况下，箭头函数反而影响了函数的作用范围的识别度，这种情况不建议使用箭头函数</li>
<li>箭头函数要实现类似纯函数的效果，必须剔除外部状态。所以箭头函数不具备普通函数里常见的 this、arguments 等，当然也就不能用 call()、apply()、bind() 去改变 this 的指向</li>
<li>箭头函数不适合定义对象的方法（对象字面量方法、对象原型方法、构造器方法），因为箭头函数没有自己的 this，其内部的 this 指向的是外层作用域的 this</li>
<li>箭头函数不适合定义结合动态上下文的回调函数（事件绑定函数），因为箭头函数在声明的时候会绑定静态上下文</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> json = &#123;<span class="attr">bar</span>: <span class="number">1</span>, <span class="attr">fn</span>: <span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="keyword">this</span>.bar)&#125;;</span><br><span class="line">json.fn(); <span class="comment">//-&gt; undefined</span></span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.bar = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Foo.prototype.fn = <span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="keyword">this</span>.foo);</span><br><span class="line"><span class="keyword">const</span> foo = <span class="keyword">new</span> Foo();</span><br><span class="line">foo.fn(); <span class="comment">//-&gt; undefined // this 并不是指向 Foo，根据变量查找规则，回溯到了全局作用域</span></span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Message = <span class="function">(<span class="params">text</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">this</span>.text = text;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> helloMessage = <span class="keyword">new</span> Message(<span class="string">'Hello World!'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(helloMessage.text); <span class="comment">//-&gt; Message is not a constructor // 不可以当作构造函数，也就是说，不可以使用 new 命令</span></span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> button = <span class="built_in">document</span>.querySelector(<span class="string">'button'</span>);</span><br><span class="line">button.addEventListener(<span class="string">'click'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">this</span>.textContent = <span class="string">'Loading...'</span>;</span><br><span class="line">&#125;); <span class="comment">// this 并不是指向预期的 button 元素，而是 window</span></span><br></pre></td></tr></table></figure>
          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/04/JS-ES6常用技巧/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/04/JS-ES6常用技巧/" itemprop="url">ECMAScript6 之常用技巧</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-04T14:43:13+08:00">
                2018-06-04
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JavaScript/" itemprop="url" rel="index">
                    <span itemprop="name">JavaScript</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>主要列举了工作开发中一些高频使用的 ECMAScript 6 的实用内容。<br>参考：<a href="http://es6.ruanyifeng.com/" target="_blank" rel="noopener">《ECMAScript 6 入门》</a></p>
<h2 id="1-let-和-const"><a href="#1-let-和-const" class="headerlink" title="1. let 和 const"></a>1. let 和 const</h2><p><code>let</code>，<code>const</code> 声明的变量只在它所在的代码块有效。</p>
<h3 id="1-1-var-存在的问题"><a href="#1-1-var-存在的问题" class="headerlink" title="1.1. var 存在的问题"></a>1.1. <code>var</code> 存在的问题</h3><ul>
<li><code>var</code> 有作用域问题（会污染全局作用域）</li>
<li><code>var</code> 可以重复声明</li>
<li><code>var</code> 会变量提升预解释</li>
<li><code>var</code> 不能定义常量</li>
</ul>
<h3 id="1-2-let、const-特性"><a href="#1-2-let、const-特性" class="headerlink" title="1.2. let、const 特性"></a>1.2. <code>let</code>、<code>const</code> 特性</h3><ul>
<li><code>let</code>、<code>const</code> 不可以重复声明</li>
<li><code>let</code>、<code>const</code> 不存在变量提升</li>
<li><code>let</code>、<code>const</code> 不会声明到全局作用域上</li>
<li><code>let</code>、<code>const</code> 存在暂时性死区（temporal dead zone，简称 TDZ）。<code>let</code> 和 <code>const</code> 声明的变量不会被提升到作用域顶部，如果在声明之前访问这些变量，会导致报错。</li>
<li><code>const</code> 做常量声明（一般常量名用大写），<code>const</code> 实际上保证的，并不是变量的值不得改动，而是变量指向的那个内存地址不得改动。</li>
</ul>
<h3 id="1-3-代码说明"><a href="#1-3-代码说明" class="headerlink" title="1.3. 代码说明"></a>1.3. 代码说明</h3><h4 id="1-3-1-作用域"><a href="#1-3-1-作用域" class="headerlink" title="1.3.1. 作用域"></a>1.3.1. 作用域</h4><p><code>var</code> 有作用域问题（会污染全局作用域），而 <code>let</code>、<code>const</code> 不会；<code>var</code> 在顶层作用域声明的变量，会直接挂在全局 <code>window</code> 对象下，成为 <code>window</code> 对象的一个属性。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// var</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.a);</span><br><span class="line"></span><br><span class="line"><span class="comment">// let、const</span></span><br><span class="line"><span class="keyword">let</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.a); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure></p>
<h4 id="1-3-2-重复声明"><a href="#1-3-2-重复声明" class="headerlink" title="1.3.2. 重复声明"></a>1.3.2. 重复声明</h4><p><code>var</code> 可以重复声明同一个变量，但 <code>let</code>、<code>const</code> 不行；<code>let</code>、<code>const</code> 不允许在相同作用域内，重复声明同一个变量。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// var</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// let、const</span></span><br><span class="line"><span class="keyword">let</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">let</span> a = <span class="number">2</span>; <span class="comment">// Uncaught SyntaxError: Identifier 'a' has already been declared</span></span><br></pre></td></tr></table></figure></p>
<h4 id="1-3-3-变量提升、暂时性死区"><a href="#1-3-3-变量提升、暂时性死区" class="headerlink" title="1.3.3. 变量提升、暂时性死区"></a>1.3.3. 变量提升、暂时性死区</h4><p><code>var</code> 存在变量提升、而 <code>let</code>、<code>const</code> 不存在，且 <code>let</code>、<code>const</code> 存在“暂时性死区”。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// var</span></span><br><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// undefined</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// let、const</span></span><br><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// Uncaught ReferenceError: a is not defined</span></span><br><span class="line"><span class="keyword">let</span> a = <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 暂时性死区</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">5</span>;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(a); <span class="comment">// Uncaught ReferenceError: a is not defined</span></span><br><span class="line">    <span class="keyword">let</span> a = <span class="number">4</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h4 id="1-3-4-常量"><a href="#1-3-4-常量" class="headerlink" title="1.3.4. 常量"></a>1.3.4. 常量</h4><p><code>var</code> 不能直接定义常量，而 <code>const</code> 可以定义常量。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> a = <span class="number">5</span>;</span><br><span class="line">a = <span class="number">6</span>; <span class="comment">// Uncaught TypeError: Assignment to constant variable</span></span><br></pre></td></tr></table></figure></p>
<h2 id="2-解构赋值"><a href="#2-解构赋值" class="headerlink" title="2. 解构赋值"></a>2. 解构赋值</h2><p>ES6 允许按照一定模式，从数组和对象中提取值，对变量进行赋值，这被称为解构（Destructuring）。本质上，只要等号两边的模式相同，左边的变量就会被赋予对应的值。<br>数组，对象，字符串，数字和布尔值都能进行结构，但常用的还是对数组和对象进行结构。</p>
<h3 id="2-1-数组的解构赋值"><a href="#2-1-数组的解构赋值" class="headerlink" title="2.1. 数组的解构赋值"></a>2.1. 数组的解构赋值</h3><p>事实上，只要某种数据结构具有 Iterator 接口，都可以采用数组形式的解构赋值。如果等号的右边不是数组（或者严格地说，不是可遍历的结构），那么将会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 通常情况</span></span><br><span class="line"><span class="keyword">let</span> [a, b, c] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line"><span class="comment">// a: 1, b: 2, c: 3</span></span><br><span class="line"><span class="keyword">let</span> [ , , c] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line"><span class="comment">// c: 3</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 嵌套数组</span></span><br><span class="line"><span class="keyword">let</span> [a, [[b], c]] = [<span class="number">1</span>, [[<span class="number">2</span>], <span class="number">3</span>]];</span><br><span class="line"><span class="comment">// a: 1, b: 2, c: 3</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 解构不成功，为 undefined</span></span><br><span class="line"><span class="keyword">let</span> [a] = [];</span><br><span class="line"><span class="comment">// a: undefined</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 不完全解构</span></span><br><span class="line"><span class="keyword">let</span> [a, [b], d] = [<span class="number">1</span>, [<span class="number">2</span>, <span class="number">3</span>], <span class="number">4</span>];</span><br><span class="line"><span class="comment">// a: 1, b: 2, d: 4</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 默认值，变量严格等于 undefined，默认值才会生效</span></span><br><span class="line"><span class="keyword">let</span> [x = <span class="number">1</span>] = [<span class="literal">undefined</span>];</span><br><span class="line">x <span class="comment">// 1</span></span><br><span class="line"><span class="keyword">let</span> [x = <span class="number">1</span>] = [<span class="literal">null</span>];</span><br><span class="line">x <span class="comment">// null</span></span><br></pre></td></tr></table></figure>
<h3 id="2-2-对象的解构赋值"><a href="#2-2-对象的解构赋值" class="headerlink" title="2.2. 对象的解构赋值"></a>2.2. 对象的解构赋值</h3><p>对象的解构与数组有一个重要的不同。数组的元素是按次序排列的，变量的取值由它的位置决定；而对象的属性没有次序，变量必须与属性同名，才能取到正确的值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 通常情况</span></span><br><span class="line"><span class="keyword">let</span> &#123; foo, bar &#125; = &#123; <span class="attr">foo</span>: <span class="string">"aaa"</span>, <span class="attr">bar</span>: <span class="string">"bbb"</span> &#125;;</span><br><span class="line"><span class="comment">// foo: "aaa" , bar: "bbb"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 变量名与属性名不一致</span></span><br><span class="line"><span class="keyword">let</span> &#123; <span class="attr">foo</span>: baz &#125; = &#123; <span class="attr">foo</span>: <span class="string">'aaa'</span>, <span class="attr">bar</span>: <span class="string">'bbb'</span> &#125;;</span><br><span class="line"><span class="comment">// baz: "aaa"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 解构不成功，为 undefined</span></span><br><span class="line"><span class="keyword">let</span> &#123; foo &#125; = &#123;<span class="attr">bar</span>: <span class="string">'baz'</span>&#125;;</span><br><span class="line"><span class="comment">// foo: undefined</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 默认值，变量严格等于 undefined，默认值才会生效</span></span><br><span class="line"><span class="keyword">var</span> &#123; <span class="attr">x</span>: y = <span class="number">3</span> &#125; = &#123;&#125;;</span><br><span class="line"><span class="comment">// y: 3</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 复杂结构</span></span><br><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">  p: [</span><br><span class="line">    <span class="string">'Hello'</span>,</span><br><span class="line">    &#123; <span class="attr">y</span>: <span class="string">'World'</span> &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> &#123; p, <span class="attr">p</span>: [x, &#123; y &#125;] &#125; = obj;</span><br><span class="line"><span class="comment">// x: "Hello", y: "World", p: ["Hello", &#123;y: "World"&#125;]</span></span><br></pre></td></tr></table></figure>
<h3 id="2-3-字符串的解构赋值"><a href="#2-3-字符串的解构赋值" class="headerlink" title="2.3. 字符串的解构赋值"></a>2.3. 字符串的解构赋值</h3><p>字符串也可以解构赋值。这是因为此时，字符串被转换成了一个类似数组的对象。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> [a, b, c, d, e, length] = <span class="string">'hello'</span>;</span><br><span class="line"><span class="comment">// a: "h" .... length: 5</span></span><br></pre></td></tr></table></figure></p>
<h2 id="3-箭头函数"><a href="#3-箭头函数" class="headerlink" title="3. 箭头函数"></a>3. 箭头函数</h2><h3 id="3-1-什么是箭头函数"><a href="#3-1-什么是箭头函数" class="headerlink" title="3.1. 什么是箭头函数"></a>3.1. 什么是箭头函数</h3><h4 id="3-1-1-箭头函数简介"><a href="#3-1-1-箭头函数简介" class="headerlink" title="3.1.1. 箭头函数简介"></a>3.1.1. 箭头函数简介</h4><p>箭头函数的语法非常简单 <code>(parameters) =&gt; { statements }</code>，箭头函数只能用赋值式写法，不能用声明式写法。看一下最简单的箭头函数表示法：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> sayHello = <span class="function"><span class="params">()</span> =&gt;</span> &#123; <span class="built_in">console</span>.log(<span class="string">'Hello World'</span>); &#125;</span><br><span class="line">sayHello(); <span class="comment">// Hello World</span></span><br></pre></td></tr></table></figure></p>
<h4 id="3-1-2-箭头函数的优点"><a href="#3-1-2-箭头函数的优点" class="headerlink" title="3.1.2. 箭头函数的优点"></a>3.1.2. 箭头函数的优点</h4><p>和普通函数相比，箭头函数主要就是以下两个方面的特点：</p>
<ul>
<li>更简化的代码语法</li>
<li>没有局部 this 的绑定</li>
<li>不绑定 arguments</li>
</ul>
<h3 id="3-2-更简化的代码语法"><a href="#3-2-更简化的代码语法" class="headerlink" title="3.2. 更简化的代码语法"></a>3.2. 更简化的代码语法</h3><h4 id="3-2-1-一般写法"><a href="#3-2-1-一般写法" class="headerlink" title="3.2.1. 一般写法"></a>3.2.1. 一般写法</h4><p><code>(parameters) =&gt; { statements }</code><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 求两个数的和并在控制台输出</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">let</span> sum = <span class="function">(<span class="params">a, b</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(a + b);</span><br><span class="line">  <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="keyword">var</span> sum = <span class="function"><span class="keyword">function</span>(<span class="params">a, b</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(a + b);</span><br><span class="line">  <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h4 id="3-2-2-省略-的情况"><a href="#3-2-2-省略-的情况" class="headerlink" title="3.2.2. 省略 () 的情况"></a>3.2.2. 省略 () 的情况</h4><p>如果只有一个参数，可以省略<code>()</code>括号。<br><code>parameters =&gt; { statements }</code><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 求一个数的两倍，并输出</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">let</span> double = <span class="function"><span class="params">num</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(num * <span class="number">2</span>);</span><br><span class="line">  <span class="keyword">return</span> num * <span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="keyword">var</span> double = <span class="function"><span class="keyword">function</span>(<span class="params">num</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(num * <span class="number">2</span>);</span><br><span class="line">  <span class="keyword">return</span> num * <span class="number">2</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h4 id="3-2-3-省略-的情况"><a href="#3-2-3-省略-的情况" class="headerlink" title="3.2.3. 省略 {} 的情况"></a>3.2.3. 省略 {} 的情况</h4><p>如果返回值仅仅只有一个表达式（expression）, 还可以省略大括号<code>{}</code>。<br><code>parameters =&gt; statements</code><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 求一个数的两倍</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">let</span> double = <span class="function"><span class="params">num</span> =&gt;</span> num * <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="keyword">var</span> double = <span class="function"><span class="keyword">function</span>(<span class="params">num</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> num * <span class="number">2</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h4 id="3-2-4-特殊情况"><a href="#3-2-4-特殊情况" class="headerlink" title="3.2.4. 特殊情况"></a>3.2.4. 特殊情况</h4><p>如果箭头函数直接返回一个对象，必须在对象外面加上括号。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">let</span> returnObj = <span class="function"><span class="params">()</span> =&gt;</span> (&#123;<span class="attr">name</span>: <span class="string">"xiguapi"</span>, <span class="attr">age</span>: <span class="number">23</span>&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="keyword">var</span> returnObj = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        name: <span class="string">"xiguapi"</span>,</span><br><span class="line">        age: <span class="number">23</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h3 id="3-3-没有局部-this-的绑定"><a href="#3-3-没有局部-this-的绑定" class="headerlink" title="3.3. 没有局部 this 的绑定"></a>3.3. 没有局部 this 的绑定</h3><p>和一般的函数不同，箭头函数不会绑定 <code>this</code>。 或则说箭头函数不会改变 <code>this</code> 本来的绑定。<br>我们用一个例子来说明：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Counter</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.num = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> Counter(); <span class="comment">// a.num 0</span></span><br></pre></td></tr></table></figure></p>
<p>因为使用了关键字 <code>new</code> 构造，<code>Count()</code> 函数中的 <code>this</code>绑定到一个新的对象，并且赋值给 <code>a</code>。通过 <code>console.log</code> 打印 <code>a.num</code>，会输出 0。</p>
<p>如果我们想每过一秒将 <code>a.num</code> 的值加 1，该如何实现呢？可以使用 <code>setInterval()</code> 函数。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Counter</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.num = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">this</span>.timer = setInterval(<span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.num++;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.num);</span><br><span class="line">  &#125;, <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = <span class="keyword">new</span> Counter();</span><br><span class="line"><span class="comment">// NaN</span></span><br><span class="line"><span class="comment">// NaN</span></span><br><span class="line"><span class="comment">// NaN</span></span><br><span class="line"><span class="comment">// ...</span></span><br></pre></td></tr></table></figure></p>
<p>首先函数 <code>setInterval</code> 没有被某个声明的对象调用，也没有使用 <code>new</code> 关键字，再之没有使用 <code>bind</code>, <code>call</code> 和 <code>apply</code>。<code>setInterval</code> 只是一个普通的函数。实际上 <code>setInterval</code> 里面的 <code>this</code> 绑定到全局对象 <code>window</code> 上了。</p>
<p>使用箭头函数！使用箭头函数就不会导致 <code>this</code> 被绑定到全局对象。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Counter</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.num = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">this</span>.timer = setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">this</span>.num++;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.num);</span><br><span class="line">  &#125;, <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> b = <span class="keyword">new</span> Counter();</span><br><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="comment">// 3</span></span><br><span class="line"><span class="comment">// ...</span></span><br></pre></td></tr></table></figure></p>
<p>通过 <code>Counter</code> 构造函数绑定的 <code>this</code> 将会被保留。在 <code>setInterval</code> 函数中，<code>this</code> 依然指向我们新创建的 <code>b</code> 对象。</p>
<h3 id="3-3-不绑定-arguments"><a href="#3-3-不绑定-arguments" class="headerlink" title="3.3. 不绑定 arguments"></a>3.3. 不绑定 arguments</h3><p>箭头函数还有一个比较有特点的地方就是其不绑定 arguments,即如果你在箭头函数中使用 arguments 参数不能得到想要的内容。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arrowfunc = <span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="built_in">arguments</span>.length)</span><br><span class="line">arrowfunc()</span><br><span class="line"><span class="comment">// arguments is not defined</span></span><br></pre></td></tr></table></figure></p>
<h3 id="3-4-适用场景及注意点"><a href="#3-4-适用场景及注意点" class="headerlink" title="3.4. 适用场景及注意点"></a>3.4. 适用场景及注意点</h3><ul>
<li>箭头函数适合于无复杂逻辑或者无副作用的纯函数场景下，例如：用在 map、reduce、filter 的回调函数定义中</li>
<li>箭头函数的亮点是简洁，但在有多层函数嵌套的情况下，箭头函数反而影响了函数的作用范围的识别度，这种情况不建议使用箭头函数</li>
<li>箭头函数要实现类似纯函数的效果，必须剔除外部状态。所以箭头函数不具备普通函数里常见的 this、arguments 等，当然也就不能用 call()、apply()、bind() 去改变 this 的指向</li>
<li>箭头函数不适合定义对象的方法（对象字面量方法、对象原型方法、构造器方法），因为箭头函数没有自己的 this，其内部的 this 指向的是外层作用域的 this</li>
<li>箭头函数不适合定义结合动态上下文的回调函数（事件绑定函数），因为箭头函数在声明的时候会绑定静态上下文</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> json = &#123;<span class="attr">bar</span>: <span class="number">1</span>, <span class="attr">fn</span>: <span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="keyword">this</span>.bar)&#125;;</span><br><span class="line">json.fn(); <span class="comment">//-&gt; undefined</span></span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.bar = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Foo.prototype.fn = <span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="keyword">this</span>.foo);</span><br><span class="line"><span class="keyword">const</span> foo = <span class="keyword">new</span> Foo();</span><br><span class="line">foo.fn(); <span class="comment">//-&gt; undefined // this 并不是指向 Foo，根据变量查找规则，回溯到了全局作用域</span></span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Message = <span class="function">(<span class="params">text</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">this</span>.text = text;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> helloMessage = <span class="keyword">new</span> Message(<span class="string">'Hello World!'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(helloMessage.text); <span class="comment">//-&gt; Message is not a constructor // 不可以当作构造函数，也就是说，不可以使用 new 命令</span></span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> button = <span class="built_in">document</span>.querySelector(<span class="string">'button'</span>);</span><br><span class="line">button.addEventListener(<span class="string">'click'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">this</span>.textContent = <span class="string">'Loading...'</span>;</span><br><span class="line">&#125;); <span class="comment">// this 并不是指向预期的 button 元素，而是 window</span></span><br></pre></td></tr></table></figure>
<h2 id="4-字符串"><a href="#4-字符串" class="headerlink" title="4. 字符串"></a>4. 字符串</h2><h3 id="4-1-includes-方法"><a href="#4-1-includes-方法" class="headerlink" title="4.1. includes 方法"></a>4.1. includes 方法</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 判断字符串中是否包含某个字符串</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">'javascript'</span>;</span><br><span class="line">str.includes(<span class="string">'sc'</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<h3 id="4-2-endsWith、startsWith-方法"><a href="#4-2-endsWith、startsWith-方法" class="headerlink" title="4.2. endsWith、startsWith 方法"></a>4.2. endsWith、startsWith 方法</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 判断字符串是否以某一个字符串开始或结束</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="string">'1AB2345CD'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(a.startsWith(<span class="string">'1A'</span>)); <span class="comment">// true</span></span><br><span class="line">a.endsWith(<span class="string">'cD'</span>) <span class="comment">// false</span></span><br></pre></td></tr></table></figure>
<h3 id="4-3-字符串内容重复输出"><a href="#4-3-字符串内容重复输出" class="headerlink" title="4.3. 字符串内容重复输出"></a>4.3. 字符串内容重复输出</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'a'</span>.repeat(<span class="number">5</span>);<span class="comment">//aaaaa 重复输出5遍</span></span><br></pre></td></tr></table></figure>
<h3 id="4-4-字符串补全"><a href="#4-4-字符串补全" class="headerlink" title="4.4. 字符串补全"></a>4.4. 字符串补全</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数1：[number] 目标字符串长度</span></span><br><span class="line"><span class="comment">// 参数2：[string] 进行补全的字符串</span></span><br><span class="line"><span class="string">'12345'</span>.padStart(<span class="number">7</span>, <span class="string">'0'</span>) <span class="comment">// 0012345 - 字符串不足7位，在头部补充不足长度的目标字符串</span></span><br><span class="line"><span class="string">'12345'</span>.padEnd(<span class="number">7</span>, <span class="string">'0'</span>) <span class="comment">// 1234500 - 在尾部进行字符串补全</span></span><br></pre></td></tr></table></figure>
<h3 id="4-5-原生支持模板语言"><a href="#4-5-原生支持模板语言" class="headerlink" title="4.5. 原生支持模板语言"></a>4.5. 原生支持模板语言</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// es5</span></span><br><span class="line">$(<span class="string">'#result'</span>).append(</span><br><span class="line">  <span class="string">'There are &lt;b&gt;'</span> + basket.count + <span class="string">'&lt;/b&gt; '</span> +</span><br><span class="line">  <span class="string">'items in your basket, '</span> +</span><br><span class="line">  <span class="string">'&lt;em&gt;'</span> + basket.onSale +</span><br><span class="line">  <span class="string">'&lt;/em&gt; are on sale!'</span></span><br><span class="line">);</span><br><span class="line"><span class="comment">// es6</span></span><br><span class="line"><span class="comment">// 在es6中，内容模板，可以定义在 `` 包起来的字符串中，其中的内容会保持原有格式</span></span><br><span class="line"><span class="comment">// 另外可以在字符串中直接使用模板语言进行变量填充，优雅而简洁</span></span><br><span class="line">$(<span class="string">'#result'</span>).append(<span class="string">`</span></span><br><span class="line"><span class="string">  There are &lt;b&gt;<span class="subst">$&#123;basket.count&#125;</span>&lt;/b&gt; items</span></span><br><span class="line"><span class="string">   in your basket, &lt;em&gt;<span class="subst">$&#123;basket.onSale&#125;</span>&lt;/em&gt;</span></span><br><span class="line"><span class="string">  are on sale!</span></span><br><span class="line"><span class="string">`</span>);</span><br></pre></td></tr></table></figure>
          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/01/自动化-Jenkins/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/01/自动化-Jenkins/" itemprop="url">自动化构建之 Jenkins™</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-01T17:06:31+08:00">
                2018-06-01
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/自动化/" itemprop="url" rel="index">
                    <span itemprop="name">自动化</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="1-使用-Jenkins-自动化构建"><a href="#1-使用-Jenkins-自动化构建" class="headerlink" title="1. 使用 Jenkins 自动化构建"></a>1. 使用 Jenkins 自动化构建</h2><h3 id="1-1-背景"><a href="#1-1-背景" class="headerlink" title="1.1. 背景"></a>1.1. 背景</h3><p>前端构建及部署工作大多都是人工去做，随着业务扩大，项目迭代速度变快，人员增多，各种问题都暴露出来，本文是对前端自动化工作流进行探索后的一篇经验分享，将通过一个简单案例分享一下基于 Jenkins 的前端自动化工作流搭建的过程，搭建完这套工作流，我们只需要在本地发起一个 git 提交，剩下的打包构建，代码部署，提醒等功能全部自动化完成，让持续集成、持续交付、持续部署变得简单易操作，真正解决人工构建部署的诸多问题。</p>
<h3 id="1-2-Jenkins-是什么"><a href="#1-2-Jenkins-是什么" class="headerlink" title="1.2. Jenkins 是什么"></a>1.2. Jenkins 是什么</h3><p>Jenkins 是一款业界流行的开源持续集成工具，广泛用于项目开发，具有自动化构建、测试和部署等功能。</p>
<h3 id="1-3-本案例要实现的功能"><a href="#1-3-本案例要实现的功能" class="headerlink" title="1.3. 本案例要实现的功能"></a>1.3. 本案例要实现的功能</h3><p>本地项目发起一个 git 提交，剩下的打包构建，代码部署等全部自动化完成。</p>
<h2 id="2-项目准备"><a href="#2-项目准备" class="headerlink" title="2. 项目准备"></a>2. 项目准备</h2><h3 id="2-1-项目准备"><a href="#2-1-项目准备" class="headerlink" title="2.1. 项目准备"></a>2.1. 项目准备</h3><p>先准备本地一个项目，我这里直接使用 vue-cli 脚手架生成了一个项目，其他技术栈也一样，只要是个项目就行。</p>
<ul>
<li>在创建项目之前，必须要有 nodeJS 环境（<a href="http://nodejs.cn/download/" target="_blank" rel="noopener">nodeJS下载地址</a>）；并将 npm 的镜像设置为国内淘宝镜像： <code>npm config set registry https://registry.npm.taobao.org</code>；</li>
<li>使用 npm 全局安装 vue 的脚手架工具，<code>npm install -g vue-cli</code>；</li>
<li>使用 npm 全局安装 webpack 的脚手架工具，<code>npm install -g webpack</code>；<br>本地创建项目 <code>vue init webpack jenkins-demo</code> ：<br><img src="http://xiezixi.oschina.io/blog/images/jenkins1.png" alt="jenkins1"><br>我们可以选择 yarn 来代替 npm；相比 npm，yar n的速度更快，更安全。可到 <a href="https://yarn.bootcss.com/" target="_blank" rel="noopener">yarn官网</a> 进行查看。<br>使用 <code>npm install</code> 安装完依赖之后，可以运行 <code>npm start</code> 来查看项目：<br><img src="http://xiezixi.oschina.io/blog/images/jenkins2.png" alt="jenkins2"></li>
</ul>
<h3 id="2-2-创建远程仓库"><a href="#2-2-创建远程仓库" class="headerlink" title="2.2. 创建远程仓库"></a>2.2. 创建远程仓库</h3><p>建立这个项目的远端git仓库，并把本地代码提交上去。可以为 github，gitlab，码云等，我这里用的是 github。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins4.png" alt="jenkins4"></p>
<h3 id="2-3-服务器环境"><a href="#2-3-服务器环境" class="headerlink" title="2.3. 服务器环境"></a>2.3. 服务器环境</h3><p>我这里用的是一台 CentOS 6.8 64 位服务器，可以使用 yum install 来安装环境配置。</p>
<ul>
<li>Java<br><code>yum install  java-1.8.0-openjdk   java-1.8.0-openjdk-devel</code>；</li>
<li>Git<br><code>yum install git</code> 拉取项目时需要；</li>
<li>Node<br><code>yum install nodejs</code> 编译前端项目；</li>
<li>Jenkins<br><code>yum install jenkins</code><br>jenkins 无非就是一个 war 包，可以在官网直接下载。</li>
</ul>
<h3 id="2-4-启动-Jenkins"><a href="#2-4-启动-Jenkins" class="headerlink" title="2.4. 启动 Jenkins"></a>2.4. 启动 Jenkins</h3><p>下载完 war 包后，可以直接通过 <code>java -jar jenkins.war</code> 来运行 Jenkins，也可以通过 tomcat 来运行 Jenkins。</p>
<ol>
<li>jenkins 的默认端口是 8080，启动成功后在浏览器打开</li>
<li>进入后会让我们输管理员密码，打开网页上提示路径下的文件，复制密码粘贴输入即可</li>
<li>然后会让安装需要的插件，此处选默认即可，等待安装完成</li>
<li>创建一个管理员账户</li>
<li>上面都完成后会看到这个界面<br><img src="http://xiezixi.oschina.io/blog/images/jenkins3.png" alt="jenkins3"><br>该案例最终 Jenkins 运行在 <code>http://111.231.238.23:8080/</code></li>
</ol>
<h3 id="2-5-创建任务"><a href="#2-5-创建任务" class="headerlink" title="2.5. 创建任务"></a>2.5. 创建任务</h3><p>点击创建一个新任务<strong>自由风格的软件项目</strong>。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins5.png" alt="jenkins3"></p>
<h2 id="3-实现-git-钩子功能"><a href="#3-实现-git-钩子功能" class="headerlink" title="3. 实现 git 钩子功能"></a>3. 实现 git 钩子功能</h2><p>首先我们要实现一个 git 钩子功能，就是我们向 github/gitlab/码云等远程仓库 push 我们的代码时，Jenkins 能知道我们提交了代码，这是自动构建自动部署的前提，钩子的实现原理是在远端仓库上配置一个 Jenkins 服务器的接口地址，当本地向远端仓库发起 push 时，远端仓库会向配置的 Jenkins 服务器的接口地址发起一个带参数的请求，Jenkins 收到后开始工作。</p>
<h3 id="3-1-添加远程仓库地址"><a href="#3-1-添加远程仓库地址" class="headerlink" title="3.1. 添加远程仓库地址"></a>3.1. 添加远程仓库地址</h3><p><img src="http://xiezixi.oschina.io/blog/images/jenkins6.png" alt="jenkins6"></p>
<h3 id="3-2-添加触发器"><a href="#3-2-添加触发器" class="headerlink" title="3.2. 添加触发器"></a>3.2. 添加触发器</h3><p>安装 Generic Webhook Trigger Plugin 插件（系统管理-插件管理-搜索 Generic Webhook Trigger Plugin ）如果可选插件列表为空，点击高级标签页。<br>第 2 步安装的触发器插件功能很强大，可以根据不同的触发参数触发不同的构建操作，比如我向远程仓库提交的是 master 分支的代码，就执行代码部署工作，我向远程仓库提交的是某个 feature 分支，就执行单元测试，单元测试通过后合并至 dev 分支。灵活性很高，可以自定义配置适合自己公司的方案，这里方便演示我们不做任何条件判断，只要有提交就触发。在任务配置里勾选 Generic Webhook Trigger 即可。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins7.png" alt="jenkins6"></p>
<h3 id="3-3-创库配置钩子"><a href="#3-3-创库配置钩子" class="headerlink" title="3.3. 创库配置钩子"></a>3.3. 创库配置钩子</h3><p>仓库配置钩子 此处以 github 为例，进入 github 项目主页后，点击 settings -&gt; Webhooks -&gt; Add webhook。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins8.png" alt="jenkins6"><br>Payload URL 格式为：http://(User ID):(API Token)@&lt;(Jenkins IP:PORT)&gt;/generic-webhook-trigger/invoke。<br>userid 和 api token 在 Jenkins 的系统管理-管理用户-admin-设置里。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins9.png" alt="jenkins6"><br>Jenkins IP 地址和端口是你部署 Jenkins 服务器的 ip 地址，端口号没改过的话就是 8080。密码填你和上面 userid 对应的密码。下面的几个选项是你在仓库执行什么操作的时候触发钩子，这里默认用 push，content-type 默认为 application/json。</p>
<h3 id="3-4-测试钩子"><a href="#3-4-测试钩子" class="headerlink" title="3.4. 测试钩子"></a>3.4. 测试钩子</h3><p><img src="http://xiezixi.oschina.io/blog/images/jenkins10.png" alt="jenkins6"><br>点击测试，如果配置是成功的，你的 Jenkins 左侧栏构建执行状态里将会出现一个任务。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins11.png" alt="jenkins6"><br>另外，你也可以试下本地提交代码，提交代码后，Jenkins 也会开始一个任务,目前我们没有配置任务开始后让它做什么，所以默认它只会在你提交新代码后，将新代码拉取到 Jenkins 服务器上。到此为止，git 钩子我们配置完成。</p>
<h2 id="4-实现自动化构建"><a href="#4-实现自动化构建" class="headerlink" title="4. 实现自动化构建"></a>4. 实现自动化构建</h2><p>git push 触发钩子后，Jenkins 就要开始工作了，自动化的构建任务可以有很多种，比如说安装升级依赖包，单元测试，e2e 测试，压缩静态资源，批量重命名等等，无论是 npm script 还是 webpack，gulp 之类的工作流，你之前在本地能做的，在这里同样可以做。</p>
<ol>
<li>首先，和本地运行 npm script 一样，我们要想在 Jenkins 里面执行 npm 命令，先要在 Jenkins 里面配置 node 的环境，当然服务器有 node 环境也可以。</li>
<li>（若服务器已经有 node 环境，可以跳过此步骤）Jenkins 里面配置 node 的环境，可以通过安装插件的方式，使用 nvm wrapper 这个插件。打开刚刚的 Jenkins 任务，点击配置里面的构建环境，勾选这个，并指定一个 node 版本。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins12.png" alt="jenkins6"></li>
<li>点击构建，把要执行的命令输进去，多个命令使用 &amp;&amp; 分开。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins13.png" alt="jenkins6"></li>
<li>保存后，此时本地修改一下代码 push 测试一下（也可以点击立即构建测试），点击本次触发的那个任务，选择控制台输出，将会看到 Jenkins 在云端执行的过程。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins14.png" alt="jenkins6"><br>命令行最后一行是 Finished 状态的如果是 SUCCESS（蓝色）则证明执行的任务都顺利进行，是 FAILURE（红色）则证明中间有重大错误导致任务失败，UNSTABLE（黄色）代表有虽然有些小问题，但不阻碍任务进行，黄色或者红色可以去命令行看下错误输出，看下哪里出了问题。<br>如果上一步是SUCCESS，点击项目的工作空间，将会发现多了 dist 和 node_modules 两个文件夹。<br>至此，我们已经搭建了一个简易的构建工作流程，构建完成了，我们需要自动化部署。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins15.png" alt="jenkins6"></li>
</ol>
<h2 id="5-实现自动化部署"><a href="#5-实现自动化部署" class="headerlink" title="5. 实现自动化部署"></a>5. 实现自动化部署</h2><h3 id="5-1-SSH-Server-配置"><a href="#5-1-SSH-Server-配置" class="headerlink" title="5.1. SSH Server 配置"></a>5.1. SSH Server 配置</h3><p>自动化部署可能是我们最需要的功能了，公司就一台服务器，我们可以使用人工部署的方式，但是如果公司有100台服务器呢，人工部署就有些吃力了，而且一旦线上出了问题，回滚也很麻烦。所以这一节实现一下自动部署的功能。</p>
<ol>
<li>首先，先在 Jenkins 上装一个插件 Publish Over SSH ，我们将通过这个工具实现服务器部署功能。</li>
<li>在要部署代码的服务器上创建一个文件夹用于接收 Jenkins 传过来的代码，我在服务器上建了一个 <code>/work/front</code> 的文件夹，用于接收传送过来的文件（注意文件的权限）。</li>
<li>Jenkins 想要往服务器上部署代码必须登录服务器才可以，这里有两种登录验证方式，一种是 ssh 验证，一种是密码验证，就像你自己登录你的服务器，你可以使用 ssh 免密登录，也可以每次输密码登录，系统管理-系统设置里找到 Publish over SSH 这一项。</li>
</ol>
<p>重点参数说明：</p>
<blockquote>
<p>Passphrase：密码（key的密码，没设置就是空）<br>  Path to key：key文件（私钥）的路径<br>  Key：将私钥复制到这个框中(path to key和key写一个即可)</p>
<p>SSH Servers 的配置：<br>  SSH Server Name：标识的名字（随便你取什么）<br>  Hostname：需要连接ssh的主机名或ip地址（建议ip）<br>  Username：用户名<br>  Remote Directory：远程目录（上面第二步建的testjenkins文件夹的路径）</p>
<p>高级配置：<br>  Use password authentication, or use a different key：勾选这个可以使用密码登录，不想配 ssh 的可以用这个先试试<br>  Passphrase / Password：密码登录模式的密码<br>  Port：端口（默认22）<br>  Timeout (ms)：超时时间（毫秒）默认300000</p>
</blockquote>
<p>配置完成后，点击 Test Configuration 测试一下是否可以连接上，如果成功会返回 success，失败会返回报错信息，根据报错信息改正即可。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins16.png" alt="jenkins6"></p>
<h3 id="5-2-构建配置"><a href="#5-2-构建配置" class="headerlink" title="5.2. 构建配置"></a>5.2. 构建配置</h3><p>接下来进入我们创建的任务，点击构建，增加 2 行代码，意思是将 dist 里面的东西打包成一个文件，因为我们要传输。<br>传输时请特别注意是否拥有被传输的文件夹的读写修改权限。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins17.png" alt="jenkins6"><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">npm install &amp;&amp;</span><br><span class="line">rm -rf dist &amp;&amp;</span><br><span class="line">npm run build</span><br><span class="line">tar -zcvf dist.tar dist</span><br></pre></td></tr></table></figure></p>
<p>点击构建后操作，增加构建后操作步骤，选择 send build artificial over SSH， 参数说明：</p>
<blockquote>
<p>Name ：选择一个你配好的ssh服务器<br>  Source files ：写你要传输的文件路径<br>  Remove prefix ：要去掉的前缀，不写远程服务器的目录结构将和 Source files 写的一致<br>  Remote directory ：写你要部署在远程服务器的那个目录地址下，不写就是 SSH Servers 配置里默认远程目录<br>  Exec command ：传输完了要执行的命令，我这里执行了解压缩和解压缩完成后删除压缩包2个命令。</p>
</blockquote>
<p><img src="http://xiezixi.oschina.io/blog/images/jenkins18.png" alt="jenkins6"></p>
<h3 id="5-3-配置-Nginx-代理静态资源"><a href="#5-3-配置-Nginx-代理静态资源" class="headerlink" title="5.3. 配置 Nginx 代理静态资源"></a>5.3. 配置 Nginx 代理静态资源</h3><p><img src="http://xiezixi.oschina.io/blog/images/jenkins19.png" alt="jenkins6"></p>
<h3 id="5-4-push-测试"><a href="#5-4-push-测试" class="headerlink" title="5.4. push 测试"></a>5.4. push 测试</h3><p>现在当我们在本地将 Welcome to Your Vue.js App 修改为 Jenkins 后发出一个 git push，过一会就会发现我们的服务器上已经部署好了最新的代码。<br><img src="http://xiezixi.oschina.io/blog/images/jenkins20.png" alt="jenkins6"><br><img src="http://xiezixi.oschina.io/blog/images/jenkins21.png" alt="jenkins6"></p>
<h2 id="6-参考文档"><a href="#6-参考文档" class="headerlink" title="6. 参考文档"></a>6. 参考文档</h2><p><a href="http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html" target="_blank" rel="noopener">持续集成是什么</a><br><a href="http://efe.baidu.com/blog/front-end-continuous-integration-tools/" target="_blank" rel="noopener">前端开源项目持续集成三剑客</a></p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/01/JS-闭包/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/01/JS-闭包/" itemprop="url">JavaScript 之闭包™</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-01T17:02:59+08:00">
                2018-06-01
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JavaScript/" itemprop="url" rel="index">
                    <span itemprop="name">JavaScript</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="1-闭包简介"><a href="#1-闭包简介" class="headerlink" title="1. 闭包简介"></a>1. 闭包简介</h2><h3 id="1-1-什么是闭包"><a href="#1-1-什么是闭包" class="headerlink" title="1.1. 什么是闭包"></a>1.1. 什么是闭包</h3><p>闭包是一种特殊的对象。它由两部分构成：函数，以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。<br>由于在 Javascript 语言中，只有函数内部的子函数才能读取函数中局部变量，因此可以把闭包简单理解成“定义在一个函数内部的函数”。<br>闭包的形成与变量的作用域以及变量的生存周期密切相关。</p>
<h3 id="1-2-变量的作用域"><a href="#1-2-变量的作用域" class="headerlink" title="1.2. 变量的作用域"></a>1.2. 变量的作用域</h3><p>变量的作用域无非就是两种：全局变量和局部变量。<br>当在函数中声明一个变量的时候，如果该变量前面没有带上关键字 <code>var</code>，这个变量就会成为全局变量，这当然是一种容易造成命名冲突的做法。<br>另外一种情况是用 <code>var</code> 关键字在函数中声明变量，这时候的变量即是局部变量，只有在该函数内部才能访问到这个变量，在函数外面是访问不到的。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> func = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line">    alert(a); <span class="comment">// 输出: 1</span></span><br><span class="line">&#125;;</span><br><span class="line">func();</span><br><span class="line">alert(a); <span class="comment">//  输出：Uncaught ReferenceError: a is not defined</span></span><br></pre></td></tr></table></figure></p>
<p>在 JavaScript 中，函数可以用来创造函数作用域。此时的函数像一层半透明的玻璃，在函数里面可以看到外面的变量，而在函数外面则无法看到函数里面的变量。这是因为当在函数中搜索一个变量的时候，如果该函数内并没有声明这个变量，那么此次搜索的过程会随着代码执行环境创建的作用域链往外层逐层搜索，一直搜索到全局对象为止。变量的搜索是从内到外而非从外到内的。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> func1 = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> b = <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">var</span> func2 = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> c = <span class="number">3</span>;</span><br><span class="line">        alert(b); <span class="comment">// 输出：2</span></span><br><span class="line">        alert(a); <span class="comment">// 输出：1</span></span><br><span class="line">    &#125;</span><br><span class="line">    func2();</span><br><span class="line">    alert(c); <span class="comment">// 输出：Uncaught ReferenceError: c is not defined</span></span><br><span class="line">&#125;;</span><br><span class="line">func1();</span><br></pre></td></tr></table></figure></p>
<h3 id="1-3-变量的生存周期"><a href="#1-3-变量的生存周期" class="headerlink" title="1.3. 变量的生存周期"></a>1.3. 变量的生存周期</h3><p>对于全局变量来说，全局变量的生存周期当然是永久的，除非我们主动销毁这个全局变量。<br>而对于在函数内用 <code>var</code> 关键字声明的局部变量来说，当退出函数时，这些局部变量即失去了它们的价值，它们都会随着函数调用的结束而被销毁：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> func = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> a = <span class="number">1</span>; <span class="comment">// 退出函数后局部变量 a 将被销毁</span></span><br><span class="line">    alert(a);</span><br><span class="line">&#125;;</span><br><span class="line">func();</span><br></pre></td></tr></table></figure></p>
<p>而闭包可以延续局部变量的生存周期：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> func = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        a++;</span><br><span class="line">        alert(a);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> f = func();</span><br><span class="line">f(); <span class="comment">// 输出：2</span></span><br><span class="line">f(); <span class="comment">// 输出：3</span></span><br><span class="line">f(); <span class="comment">// 输出：4</span></span><br><span class="line">f(); <span class="comment">// 输出：5</span></span><br></pre></td></tr></table></figure></p>
<p>当退出函数后，局部变量 <code>a</code> 并没有消失，而是似乎一直在某个地方存活着。这是因为当执行 <code>var f = func();</code> 时，f 返回了一个匿名函数的引用，它可以访问到 <code>func()</code>被调用时产生的环境，而局部变量 <code>a</code> 一直处在这个环境里。既然局部变量所在的环境还能被外界访问，这个局部变量就有了不被销毁的理由。在这里产生了一个闭包结构，局部变量的生命看起来被延续了。</p>
<h2 id="2-闭包的主要作用"><a href="#2-闭包的主要作用" class="headerlink" title="2. 闭包的主要作用"></a>2. 闭包的主要作用</h2><h3 id="2-1-封装变量"><a href="#2-1-封装变量" class="headerlink" title="2.1. 封装变量"></a>2.1. 封装变量</h3><p>任何在函数中定义的变量，都可以认为是私有变量，因为不能在函数外部访问这些变量。私有变量包括函数的参数、局部变量和函数内定义的其他函数。<br>把有权访问私有变量的公有方法称为特权方法。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Student</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 私有变量</span></span><br><span class="line">    <span class="keyword">var</span> name = <span class="string">"学生"</span>;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">sayHello</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"hello"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 特权方法</span></span><br><span class="line">    <span class="keyword">this</span>.getName = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> student = <span class="keyword">new</span> Student();</span><br><span class="line"><span class="built_in">console</span>.log(student.name); <span class="comment">// undefined</span></span><br><span class="line"><span class="built_in">console</span>.log(student.getName());  <span class="comment">// 学生</span></span><br></pre></td></tr></table></figure></p>
<h3 id="2-2-减少全局变量"><a href="#2-2-减少全局变量" class="headerlink" title="2.2. 减少全局变量"></a>2.2. 减少全局变量</h3><p>使用闭包模块化代码，可以减少全局变量的污染。闭包可以帮助把一些不需要暴露在全局的变量封装成“私有变量”。<br>以加入缓存机制的求和函数为例。<br>没有使用闭包时需要定义全局变量：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> cache = &#123;&#125;; <span class="comment">// 缓存</span></span><br><span class="line"><span class="keyword">var</span> sum = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> index = <span class="built_in">Array</span>.prototype.join.call(<span class="built_in">arguments</span>, <span class="string">","</span>);</span><br><span class="line">    <span class="keyword">if</span> (cache[index]) &#123;</span><br><span class="line">        <span class="keyword">return</span> cache[index];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">var</span> sum = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">arguments</span>.length; i++) &#123;</span><br><span class="line">        sum += <span class="built_in">arguments</span>[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> cache[index] = sum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>我们看到 <code>cache</code> 这个变量仅仅在 <code>sum</code> 函数中被使用，与其让 <code>cache</code> 变量跟 <code>sum</code> 函数一起平行地暴露在全局作用域下，不如把它封闭在 <code>sum</code> 函数内部，这样可以减少页面中的全局变量，以避免这个变量在其他地方被不小心修改而引发错误。代码如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> sum = (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> cache = &#123;&#125;;</span><br><span class="line">    <span class="keyword">var</span> calculate = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> sum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">arguments</span>.length; i++) &#123;</span><br><span class="line">            sum += <span class="built_in">arguments</span>[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> index = <span class="built_in">Array</span>.prototype.join.call(<span class="built_in">arguments</span>, <span class="string">","</span>);</span><br><span class="line">        <span class="keyword">if</span> (cache[index]) &#123;</span><br><span class="line">            <span class="keyword">return</span> cache[index];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> cache[index] = calculate.apply(<span class="literal">null</span>, <span class="built_in">arguments</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure></p>
<h3 id="2-3-延续局部变量的寿命"><a href="#2-3-延续局部变量的寿命" class="headerlink" title="2.3. 延续局部变量的寿命"></a>2.3. 延续局部变量的寿命</h3><p>利用闭包我们可以完成许多奇妙的工作，下面介绍一个闭包的经典应用。假设页面上有 5 个 div 节点，我们通过循环来给每个 div 绑定 onclick 事件，按照索引顺序，点击第 1 个 div 时弹出0，点击第 2 个 div 时弹出 1，以此类推。代码如下：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span>1<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span>2<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span>3<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span>4<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span>5<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="javascript">    <span class="keyword">var</span> nodes = <span class="built_in">document</span>.getElementsByTagName(<span class="string">'div'</span>);</span></span><br><span class="line"><span class="javascript">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, len = nodes.length; i &lt; len; i++) &#123;</span></span><br><span class="line"><span class="javascript">        nodes[i].onclick = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span></span><br><span class="line"><span class="undefined">            alert(i);</span></span><br><span class="line"><span class="undefined">        &#125;</span></span><br><span class="line"><span class="undefined">    &#125;;</span></span><br><span class="line"><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>测试这段代码就会发现，无论点击哪个 div，最后弹出的结果都是 5。这是因为 div 节点的onclick 事件是被异步触发的，当事件被触发的时候，for 循环早已结束，此时变量 i 的值已经是5，所以在 div 的 onclick 事件函数中顺着作用域链从内到外查找变量 i 时，查找到的值总是 5。<br>解决方法是在闭包的帮助下，把每次循环的 i 值都封闭起来。当在事件函数中顺着作用域链中从内到外查找变量 i 时，会先找到被封闭在闭包环境中的 i，如果有 5 个 div，这里的 i 就分别是 0,1,2,3,4：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, len = nodes.length; i &lt; len; i++) &#123;</span><br><span class="line">    (<span class="function"><span class="keyword">function</span> (<span class="params">i</span>) </span>&#123;</span><br><span class="line">        nodes[i].onclick = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)(i)</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h2 id="3-销毁闭包"><a href="#3-销毁闭包" class="headerlink" title="3. 销毁闭包"></a>3. 销毁闭包</h2><p>通常，函数的作用域及其所有变量都会在函数执行结束后被销毁。但是，在创建了一个闭包以后，这个函数的作用域就会一直保存到闭包不存在为止。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">makeAdder</span>(<span class="params">x</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">y</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> x + y;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> add5 = makeAdder(<span class="number">5</span>);</span><br><span class="line"><span class="keyword">var</span> add10 = makeAdder(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(add5(<span class="number">2</span>));  <span class="comment">// 7</span></span><br><span class="line"><span class="built_in">console</span>.log(add10(<span class="number">2</span>)); <span class="comment">// 12</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 释放对闭包的引用</span></span><br><span class="line">add5 = <span class="literal">null</span>;</span><br><span class="line">add10 = <span class="literal">null</span>;</span><br></pre></td></tr></table></figure></p>
<p>add5 和 add10 都是闭包。它们共享相同的函数定义，但是保存了不同的环境。在 add5 的环境中，x 为 5。而在 add10 中，x 则为 10。最后通过 null 释放了 add5 和 add10 对闭包的引用。</p>
<h2 id="4-缺陷"><a href="#4-缺陷" class="headerlink" title="4. 缺陷"></a>4. 缺陷</h2><ul>
<li>闭包的缺点就是常驻内存会增大内存使用量，并且使用不当很容易造成内存泄露。</li>
<li>如果不是因为某些特殊任务而需要闭包，在没有必要的情况下，在其它函数中创建函数是不明智的，因为闭包对脚本性能具有负面影响，包括处理速度和内存消耗。</li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/01/JS-深浅拷贝/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/01/JS-深浅拷贝/" itemprop="url">JavaScript 之深浅拷贝™</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-01T17:01:00+08:00">
                2018-06-01
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JavaScript/" itemprop="url" rel="index">
                    <span itemprop="name">JavaScript</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="1-JavaScript-数据类型"><a href="#1-JavaScript-数据类型" class="headerlink" title="1. JavaScript 数据类型"></a>1. JavaScript 数据类型</h2><p>Javascript 有六种基本数据类型（也就是简单数据类型），它们分别是：Undefined，Null，Boolean，Symbol，Number 和 String。还含有一种复杂数据类型，就是 Object（对象）。</p>
<p>注意 Undefined 和 Null 的区别，Undefined 类型只有一个值，就是 <code>undefined</code>，Null 类型也只有一个值，也就是 <code>null</code>。</p>
<ul>
<li><code>undefined</code> 实就是已声明未赋值的变量输出的结果</li>
<li><code>null</code> 空对象的引用</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a;</span><br><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// undefined</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">document</span>.getElementById(<span class="string">'a'</span>)); <span class="comment">// null</span></span><br></pre></td></tr></table></figure>
<h2 id="2-数据类型的区别"><a href="#2-数据类型的区别" class="headerlink" title="2. 数据类型的区别"></a>2. 数据类型的区别</h2><h3 id="2-1-简单数据类型"><a href="#2-1-简单数据类型" class="headerlink" title="2.1. 简单数据类型"></a>2.1. 简单数据类型</h3><p>它们值在占据了内存中固定大小的空间，并被保存在栈内存中。当一个变量向另一个变量复制基本类型的值，会创建这个值的一个副本，还有就是不能给基本数据类型的值添加属性。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> b = a;</span><br><span class="line">a.attr = <span class="string">'hello'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(a.attr); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure></p>
<p>上面代码中 a 就是简单数据类型（Number），b 就是 a 的副本，它们两者都占有不同位置但相等的内存空间。</p>
<h3 id="2-2-复杂的数据类型"><a href="#2-2-复杂的数据类型" class="headerlink" title="2.2. 复杂的数据类型"></a>2.2. 复杂的数据类型</h3><p>复杂的数据类型即引用类型，它的值是对象，保存在堆内存中，包含引用类型值的变量实际上包含的并不是对象本身，而是一个指向该对象的指针。从一个变量向另一个变量复制引用类型的值，复制的其实是指针，因此两个变量最终都指向同一个对象。</p>
<h2 id="3-浅拷贝"><a href="#3-浅拷贝" class="headerlink" title="3. 浅拷贝"></a>3. 浅拷贝</h2><p>下面这段代码就是浅拷贝，有时候我们只是想备份数组，但是只是简单让它赋给一个变量，改变其中一个，另外一个就紧跟着改变，但很多时候这不是我们想要的。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    name: <span class="string">'xiguapi'</span>,</span><br><span class="line">    age: <span class="number">23</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj2 = obj;</span><br><span class="line">obj2[<span class="string">'age'</span>] = <span class="number">22</span>;</span><br><span class="line"><span class="built_in">console</span>.log(obj);<span class="comment">// &#123; name: 'xiguapi', age: 22 &#125;</span></span><br><span class="line"><span class="built_in">console</span>.log(obj2);<span class="comment">// &#123; name: 'xiguapi', age: 22 &#125;</span></span><br></pre></td></tr></table></figure></p>
<p>我们可以看到 obj 赋值给 obj2 后，当我们更改其中一个对象的属性值，两个对象都发生了改变，究其原因局势因为 obj 和 obj2 这两个变量都指向同一个指针，赋值只是复制了指针，所以当我们改变其中一个的值就会影响另外一个变量的值。</p>
<p>ES6 中的 <code>Object.assign()</code> 只是一级属性复制，比浅拷贝多深拷贝了一层而已。不能使用 <code>Object.assign()</code> 进行深拷贝。</p>
<h2 id="4-深拷贝"><a href="#4-深拷贝" class="headerlink" title="4. 深拷贝"></a>4. 深拷贝</h2><h3 id="4-1-基本类型数组"><a href="#4-1-基本类型数组" class="headerlink" title="4.1. 基本类型数组"></a>4.1. 基本类型数组</h3><p>对于<strong>基本数据类型数组</strong>我们可以使用 <code>slice()</code> 和 <code>concat()</code> 方法进行深拷贝。</p>
<h4 id="4-1-1-slice"><a href="#4-1-1-slice" class="headerlink" title="4.1.1. slice"></a>4.1.1. slice</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="string">'xiguapi'</span>, <span class="number">22</span>];</span><br><span class="line"><span class="keyword">var</span> arrCopy = arr.slice();</span><br><span class="line">arrCopy[<span class="number">1</span>] = <span class="number">23</span>;</span><br><span class="line"><span class="built_in">console</span>.log(arr); <span class="comment">// [ 'xiguapi', 22 ]</span></span><br><span class="line"><span class="built_in">console</span>.log(arrCopy); <span class="comment">// [ 'xiguapi', 23 ]</span></span><br></pre></td></tr></table></figure>
<h4 id="4-1-2-concat"><a href="#4-1-2-concat" class="headerlink" title="4.1.2. concat"></a>4.1.2. concat</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="string">'xiguapi'</span>, <span class="number">22</span>];</span><br><span class="line"><span class="keyword">var</span> arrCopy = arr.concat();</span><br><span class="line">arrCopy[<span class="number">0</span>] = <span class="string">'xiezixi'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(arr); <span class="comment">// [ 'xiguapi', 22 ]</span></span><br><span class="line"><span class="built_in">console</span>.log(arrCopy); <span class="comment">// [ 'xiezixi', 22 ]</span></span><br></pre></td></tr></table></figure>
<h3 id="4-2-对象"><a href="#4-2-对象" class="headerlink" title="4.2. 对象"></a>4.2. 对象</h3><h4 id="4-2-1-封装方法"><a href="#4-2-1-封装方法" class="headerlink" title="4.2.1. 封装方法"></a>4.2.1. 封装方法</h4><p>对象我们可以定义一个封装好的方法来处理对象的深拷贝。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> deepCopy = <span class="function"><span class="keyword">function</span> (<span class="params">source</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> result = &#123;&#125;;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> source) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> source[key] === <span class="string">'object'</span>) &#123;</span><br><span class="line">            result[key] = deepCopy(source[key])</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            result[key] = source[key]</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    name: <span class="string">'xiguapi'</span>,</span><br><span class="line">    age: <span class="number">23</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj1 = deepCopy(obj);</span><br><span class="line">obj1[<span class="string">'age'</span>] = <span class="number">22</span>;</span><br><span class="line"><span class="built_in">console</span>.log(obj); <span class="comment">// &#123; name: 'xiguapi', age: 23 &#125;</span></span><br><span class="line"><span class="built_in">console</span>.log(obj1); <span class="comment">// &#123; name: 'xiguapi', age: 22 &#125;</span></span><br></pre></td></tr></table></figure>
<h4 id="4-2-2-JSON"><a href="#4-2-2-JSON" class="headerlink" title="4.2.2. JSON"></a>4.2.2. JSON</h4><p>如果数据中没有函数，可以直接用JSON来实现拷贝，如果有函数，就得用递归来实现深拷贝。需要兼容不同的浏览器并考虑速度的话，推荐使用 <code>JSON.parse(JSON.stringify())</code>。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    name: <span class="string">'xiguapi'</span>,</span><br><span class="line">    age: <span class="number">23</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj1 = <span class="built_in">JSON</span>.parse(<span class="built_in">JSON</span>.stringify(obj));</span><br><span class="line">obj1[<span class="string">'age'</span>] = <span class="number">22</span>;</span><br><span class="line"><span class="built_in">console</span>.log(obj); <span class="comment">// &#123; name: 'xiguapi', age: 23 &#125;</span></span><br><span class="line"><span class="built_in">console</span>.log(obj1); <span class="comment">// &#123; name: 'xiguapi', age: 22 &#125;</span></span><br></pre></td></tr></table></figure></p>
<h4 id="4-2-3-jQuery-clone"><a href="#4-2-3-jQuery-clone" class="headerlink" title="4.2.3. jQuery $.clone()"></a>4.2.3. jQuery $.clone()</h4><p>在 jQuery 中也有这么一个叫 <code>$.clone()</code> 的方法，可是它并不是用于一般的 JS 对象的深拷贝，而是用于 DOM 对象。</p>
<h4 id="4-2-4-jQuery-extend"><a href="#4-2-4-jQuery-extend" class="headerlink" title="4.2.4. jQuery $.extend()"></a>4.2.4. jQuery $.extend()</h4><p>可以通过 <code>$.extend()</code> 方法来完成深拷贝。值得庆幸的是，我们在 jQuery 中可以通过添加一个参数来实现递归 extend。调用 <code>$.extend(true, {}, ...)</code> 就可以实现深拷贝。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">       name: <span class="string">'xiguapi'</span>,</span><br><span class="line">       age: <span class="number">23</span></span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">var</span> obj1 = $.extend(<span class="literal">true</span>, &#123;&#125;, obj);</span><br><span class="line">   obj1[<span class="string">'age'</span>] = <span class="number">22</span>;</span><br><span class="line">   <span class="built_in">console</span>.log(obj); <span class="comment">// &#123; name: 'xiguapi', age: 23 &#125;</span></span><br><span class="line">   <span class="built_in">console</span>.log(obj1); <span class="comment">// &#123; name: 'xiguapi', age: 22 &#125;</span></span><br></pre></td></tr></table></figure></p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/01/HTML5-ApplicationCache/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/01/HTML5-ApplicationCache/" itemprop="url">HTML5 之 Application Cache™</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-01T16:56:26+08:00">
                2018-06-01
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/HTML5/" itemprop="url" rel="index">
                    <span itemprop="name">HTML5</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="1-Application-Cache-简介"><a href="#1-Application-Cache-简介" class="headerlink" title="1. Application Cache 简介"></a>1. Application Cache 简介</h2><h3 id="1-1-什么是-Application-Cache"><a href="#1-1-什么是-Application-Cache" class="headerlink" title="1.1. 什么是 Application Cache"></a>1.1. 什么是 Application Cache</h3><p>HTML5 引入了应用程序缓存，这意味着 web 应用可进行缓存，并可在没有因特网连接时进行访问。</p>
<p>应用程序缓存为应用带来三个优势：</p>
<ul>
<li><strong>离线浏览</strong> - 用户可在应用离线时使用它们</li>
<li><strong>速度</strong> - 已缓存资源加载得更快</li>
<li><strong>减少服务器负载</strong> - 浏览器将只从服务器下载更新过或更改过的资源</li>
</ul>
<h3 id="1-2-应用缓存原理"><a href="#1-2-应用缓存原理" class="headerlink" title="1.2. 应用缓存原理"></a>1.2. 应用缓存原理</h3><p>HTML5 的离线存储是基于一个 manifest 文件（缓存清单文件，后缀为 .appcache）的缓存机制（不是存储技术），通过这个文件上的清单解析离线存储资源，这些资源就会像 cookie 一样被存储了下来。之后当网络在处于离线状态时，浏览器会通过被离线存储的数据进行页面展示。</p>
<h3 id="1-3-使用方法"><a href="#1-3-使用方法" class="headerlink" title="1.3. 使用方法"></a>1.3. 使用方法</h3><ul>
<li>manifest 文件的建议的文件扩展名是：<strong>.appcache</strong></li>
<li>首先在文档的 html 标签中设置 manifest 属性，引用 manifest 文件</li>
<li>然后配置 manifest 文件，在 manifest 文件中编写离线存储的资源</li>
<li>此外，必须要在服务器端正确的配置 MIME-type。即 <strong>text/cache-manifest</strong></li>
</ul>
<h3 id="1-4-浏览器兼容性"><a href="#1-4-浏览器兼容性" class="headerlink" title="1.4. 浏览器兼容性"></a>1.4. 浏览器兼容性</h3><p><a href="https://caniuse.com/#search=Offline%20web%20applications" target="_blank" rel="noopener">Offline Web Applications 浏览器兼容性</a></p>
<h2 id="2-Manifest-文件"><a href="#2-Manifest-文件" class="headerlink" title="2. Manifest 文件"></a>2. Manifest 文件</h2><p>manifest 文件是简单的文本文件，它告知浏览器被缓存的内容（以及不缓存的内容）。</p>
<p>manifest 文件可分为三个部分：</p>
<ul>
<li><strong>CACHE MANIFEST</strong> - 在此标题下列出的文件将在首次下载后进行缓存</li>
<li><strong>NETWORK</strong> -  在此标题下列出的文件需要与服务器的连接，且不会被缓存</li>
<li><strong>FALLBACK</strong> -  在此标题下列出的文件规定当页面无法访问时的回退页面（比如 404 页面）</li>
</ul>
<h2 id="3-简单实例"><a href="#3-简单实例" class="headerlink" title="3. 简单实例"></a>3. 简单实例</h2><p>接下来，用具体实例来看一下如何使用离线缓存。</p>
<p>文件夹目录如下：</p>
<p><img src="http://xiezixi.oschina.io/blog/images/cache1.png" alt="文件夹目录"></p>
<p><strong>cache.html</strong> 代码如下：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!doctype html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">manifest</span>=<span class="string">"cache.appcache"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>应用缓存<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"cache.css"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"cache.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">"cache.png"</span> <span class="attr">alt</span>=<span class="string">"cache.png"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p><strong>cache.appcache</strong> 代码如下：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">CACHE MANIFEST</span><br><span class="line"># v0.0.1</span><br><span class="line">CACHE:</span><br><span class="line">/cache.css</span><br><span class="line">/cache.js</span><br><span class="line">/cache.png</span><br><span class="line"></span><br><span class="line">NETWORK:</span><br><span class="line">*</span><br></pre></td></tr></table></figure></p>
<p>manifest 属性可指向绝对网址或相对路径，但绝对网址必须与相应的网络应用同源。manifest 除了缓存 manifest.appcache 文件所指定的资源外，还必定会缓存当前的 html 页面。可以使用星号来指示所有其他资源/文件都需要因特网连接。</p>
<p>注意：这里的版本号，是我们人为规定的，而非是 manifest 自带的属性，当每一次 html 加载到 manifest 时，会对 manifest 配置文件进行脏检查，当检测到 manifest 文件被修改后，之前的缓存将会被弃用，转而去根据 manifest 文件中配置的新内容进行缓存。</p>
<p><strong>缓存查看</strong><br>使用 node 或其他方法启动服务后，访问页面之后尝试关闭网络环境，依然能够访问。</p>
<p><img src="http://xiezixi.oschina.io/blog/images/cache2.png" alt="缓存查看1"></p>
<p><img src="http://xiezixi.oschina.io/blog/images/cache3.png" alt="缓存查看2"></p>
<p><img src="http://xiezixi.oschina.io/blog/images/cache4.png" alt="缓存查看3"></p>
<h2 id="4-window-applicationCache"><a href="#4-window-applicationCache" class="headerlink" title="4. window.applicationCache"></a>4. window.applicationCache</h2><p>window.applicationCache 对象是对浏览器的应用缓存的编程访问方式。其 status 属性可用于查看缓存的当前状态。</p>
<h3 id="4-1-status-属性值"><a href="#4-1-status-属性值" class="headerlink" title="4.1. status 属性值"></a>4.1. status 属性值</h3><table>
<thead>
<tr>
<th>属性值</th>
<th>含义</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td><code>UNCACHED</code></td>
<td>无缓存， 即没有与页面相关的应用缓存</td>
</tr>
<tr>
<td>1</td>
<td><code>IDLE</code></td>
<td>闲置，即应用缓存未得到更新</td>
</tr>
<tr>
<td>2</td>
<td><code>CHECKING</code></td>
<td>检查中，即正在下载描述文件并检查更新</td>
</tr>
<tr>
<td>3</td>
<td><code>DOWNLOADING</code></td>
<td>下载中，即应用缓存正在下载描述文件中指定的资源</td>
</tr>
<tr>
<td>4</td>
<td><code>UPDATEREADY</code></td>
<td>更新完成，所有资源都已下载完毕</td>
</tr>
<tr>
<td>5</td>
<td><code>IDLE</code></td>
<td>废弃，即应用缓存的描述文件已经不存在了，因此页面无法再访问应用缓存</td>
</tr>
</tbody>
</table>
<p>以下代码使用 status 属性为当前通过网页所加载的文档确定应用程序缓存的状态</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> oAppCache = <span class="built_in">window</span>.applicationCache;</span><br><span class="line"><span class="keyword">var</span> sCacheStatus = <span class="string">"Not supported"</span>;</span><br><span class="line"><span class="keyword">switch</span> (oAppCache.status) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">0</span>: <span class="comment">// UNCACHED == 0</span></span><br><span class="line">        sCacheStatus = <span class="string">'（UNCACHED） : 无缓存， 即没有与页面相关的应用缓存'</span>;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">1</span>: <span class="comment">// IDLE == 1</span></span><br><span class="line">        sCacheStatus = <span class="string">'（IDLE） : 闲置，即应用缓存未得到更新'</span>;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">2</span>: <span class="comment">// CHECKING == 2</span></span><br><span class="line">        sCacheStatus = <span class="string">'（CHECKING） : 检查中，即正在下载描述文件并检查更新'</span>;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">3</span>: <span class="comment">// DOWNLOADING == 3</span></span><br><span class="line">        sCacheStatus = <span class="string">'（DOWNLOADING） : 下载中，即应用缓存正在下载描述文件'</span>;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">4</span>: <span class="comment">// UPDATEREADY == 4</span></span><br><span class="line">        sCacheStatus = <span class="string">'（UPDATEREADY） : 更新完成，所有资源都已下载完毕'</span>;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">5</span>: <span class="comment">// OBSOLETE == 5</span></span><br><span class="line">        sCacheStatus = <span class="string">'（IDLE） : 废弃，即应用缓存的描述文件已经不存在了，因此页面无法再访问应用缓存'</span>;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">        sCacheStatus = <span class="string">'（UKNOWN） ： CACHE STATUS '</span>;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="4-2-APPCACHE-事件"><a href="#4-2-APPCACHE-事件" class="headerlink" title="4.2 APPCACHE 事件"></a>4.2 APPCACHE 事件</h3><p>浏览器会对下载进度、应用缓存更新和错误状态等情况触发相应事件。</p>
<table>
<thead>
<tr>
<th>事件</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>checking</code></td>
<td>每当应用程序载入的时候，都会检查该清单文件,也总会首先触发<code>checking</code>事件。</td>
</tr>
<tr>
<td><code>noupdate</code></td>
<td>如果没有改动，同时应用程序也已经缓存了<code>noupdate</code>事件被触发，整个过程结束。</td>
</tr>
<tr>
<td><code>downloading</code></td>
<td>如果还未缓存应用程序，或者清单文件有改动,那么浏览器会下载并缓存清单中的所有资源 ,触发<code>downloading</code>事件，同时意味着下载过程开始。</td>
</tr>
<tr>
<td><code>progress</code></td>
<td>在下载过程中会间断性触发<code>progress</code>事件，通常是在每个文件下载完毕的时候。</td>
</tr>
<tr>
<td><code>cached</code></td>
<td>下载完成并且首次将应用程序下载到缓存中时，浏览器会触发<code>cached</code>事件。</td>
</tr>
<tr>
<td><code>updateready</code></td>
<td>当下载完成并将缓存中的应用程序更新后，浏览器会触发<code>updaterady</code>事件。</td>
</tr>
<tr>
<td><code>error</code></td>
<td>如果浏览器处于离线状态，检查清单列表失败，则会触发<code>error</code>事件，当一个未缓存的应用程序引用一个不存在的清单文件，也会触发此事件。</td>
</tr>
<tr>
<td><code>obsolete</code></td>
<td>如果一个缓存的应用程序引用一个不存在的清单文件，会触发<code>obsolete</code>，同时将应用从缓存中移除之后不会从缓存而是通过网络加载资。</td>
</tr>
</tbody>
</table>
<p>以下代码段为每种缓存事件类型设置了事件监听器：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleCacheEvent</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'对应操作'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleCacheError</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'Error: Cache failed to update!'</span>);</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//在浏览器为应用缓存查找更新时触发</span></span><br><span class="line">oAppCache.addEventListener(<span class="string">'checking'</span>, handleCacheEvent, <span class="literal">false</span>);</span><br><span class="line"><span class="comment">//在检查描述文件发现文件无变化时触发</span></span><br><span class="line">oAppCache.addEventListener(<span class="string">'noupdate'</span>, handleCacheEvent, <span class="literal">false</span>);</span><br><span class="line"><span class="comment">// 在开始下载应用缓存资源时触发</span></span><br><span class="line">oAppCache.addEventListener(<span class="string">'downloading'</span>, handleCacheEvent, <span class="literal">false</span>);</span><br><span class="line"><span class="comment">//在文件下载应用缓存的过程中持续不断地下载地触发</span></span><br><span class="line">oAppCache.addEventListener(<span class="string">'progress'</span>, handleCacheEvent, <span class="literal">false</span>);</span><br><span class="line"><span class="comment">//在应用缓存完整可用时触发</span></span><br><span class="line">oAppCache.addEventListener(<span class="string">'cached'</span>, handleCacheEvent, <span class="literal">false</span>);</span><br><span class="line"><span class="comment">//在页面新的应用缓存下载完毕触发</span></span><br><span class="line">oAppCache.addEventListener(<span class="string">'updateready'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    oAppCache.swapCache();<span class="comment">// 更新本地缓存</span></span><br><span class="line">    location.reload();    <span class="comment">//重新加载页面页面</span></span><br><span class="line">&#125;, <span class="literal">false</span>);</span><br><span class="line"><span class="comment">//在检查更新或下载资源期间发送错误时触发</span></span><br><span class="line">oAppCache.addEventListener(<span class="string">'error'</span>, handleCacheError, <span class="literal">false</span>);</span><br><span class="line"><span class="comment">//缓存清单不存在时触发</span></span><br><span class="line">oAppCache.addEventListener(<span class="string">'obsolete'</span>, handleCacheEvent, <span class="literal">false</span>);</span><br></pre></td></tr></table></figure>
<h2 id="5-更新缓存"><a href="#5-更新缓存" class="headerlink" title="5. 更新缓存"></a>5. 更新缓存</h2><p>一旦应用被缓存，它就会保持缓存直到发生下列情况：</p>
<ul>
<li>用户清空浏览器缓存</li>
<li>manifest 文件被修改</li>
<li>由程序来更新应用缓存</li>
</ul>
<p>以 “#” 开头的是注释行，但也可满足其他用途。应用的缓存会在其 manifest 文件更改时被更新。如果您编辑了一幅图片，或者修改了一个 JavaScript 函数，这些改变都不会被重新缓存。更新注释行中的日期和版本号是一种使浏览器重新缓存文件的办法。</p>
<p>由程序来更新应用缓存:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">oAppCache.addEventListener(<span class="string">'updateready'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">          oAppCache.swapCache();<span class="comment">// 更新本地缓存</span></span><br><span class="line">          location.reload();    <span class="comment">//重新加载页面页面</span></span><br><span class="line">&#125;, <span class="literal">false</span>);</span><br></pre></td></tr></table></figure>
<h2 id="6-注意事项"><a href="#6-注意事项" class="headerlink" title="6. 注意事项"></a>6. 注意事项</h2><ul>
<li>更新清单中列出的某个文件并不意味着浏览器会重新缓存该资源，清单文件本身必须进行更改。</li>
<li>浏览器对缓存数据的容量限制可能不太一样（某些浏览器设置的限制是每个站点 5MB）。</li>
<li>如果manifest文件，或者内部列举的某一个文件不能正常下载，整个更新过程都将失败，浏览器继续全部使用老的缓存。</li>
<li>引用 manifest 的 html 必须与 manifest 文件同源，在同一个域下。FALLBACK 中的资源必须和 manifest 文件同源。</li>
<li>浏览器会自动缓存引用 manifest 文件的 HTML 文件，这就导致如果改了 HTML 内容，也需要更新 manifest  文件版本或者由程序来更新应用缓存才能做到更新。</li>
<li>若遇到如此报错 <strong>“Application Cache Error event: Manifest fetch failed (404)”</strong> ,其原因是 manifest 文件需要正确的配置 MIME-type（描述该消息的媒体类型），即 <strong>“text/cache-manifest”</strong> ，必须在服务器端进行配置。不同服务器配置方式不一样。</li>
</ul>
<p>在tomcat服务器中的conf/web.xml中添加：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mime-mapping</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">extension</span>&gt;</span>manifest<span class="tag">&lt;/<span class="name">extension</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">mime-type</span>&gt;</span>text/cache-manifest<span class="tag">&lt;/<span class="name">mime-type</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mime-mapping</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h2 id="7-与传统浏览器缓存区别"><a href="#7-与传统浏览器缓存区别" class="headerlink" title="7. 与传统浏览器缓存区别"></a>7. 与传统浏览器缓存区别</h2><ul>
<li>离线缓存是针对整个应用，浏览器缓存是单个文件。</li>
<li>离线缓存断网了还是可以打开页面，浏览器缓存不行。</li>
<li>离线缓存可以主动通知浏览器更新资源。</li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/01/HTML5-WebWorker/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/01/HTML5-WebWorker/" itemprop="url">HTML5 之 Web Worker</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-01T16:53:48+08:00">
                2018-06-01
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/HTML5/" itemprop="url" rel="index">
                    <span itemprop="name">HTML5</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="1-Web-Worker-简介"><a href="#1-Web-Worker-简介" class="headerlink" title="1. Web Worker 简介"></a>1. Web Worker 简介</h2><h3 id="1-1-什么是-Web-Worker"><a href="#1-1-什么是-Web-Worker" class="headerlink" title="1.1. 什么是 Web Worker"></a>1.1. 什么是 Web Worker</h3><p>Web Worker 是 HTML5 标准的一部分，这一规范定义了一套 API，它允许一段 JavaScript 程序运行在主线程之外的另外一个线程中。工作线程允许开发人员编写能够长时间运行而不被用户所中断的后台程序， 去执行事务或者逻辑，并同时保证页面对用户的及时响应，可以将一些大量计算的代码交给 web worker 运行而不冻结用户界面。</p>
<h3 id="1-2-Web-Worker-问世背景"><a href="#1-2-Web-Worker-问世背景" class="headerlink" title="1.2. Web Worker 问世背景"></a>1.2. Web Worker 问世背景</h3><p>JavaScript 引擎是单线程运行的，JavaScript 中耗时的 I/O 操作都被处理为异步操作，它们包括键盘、鼠标 I/O 输入输出事件、窗口大小的 resize 事件、定时器(setTimeout、setInterval)事件、Ajax 请求网络 I/O回调等。当这些异步任务发生的时候，它们将会被放入浏览器的事件任务队列中去，等到JavaScript运行时执行线程空闲时候才会按照队列先进先出的原则被一一执行，但终究还是单线程。</p>
<p>很多人觉得异步（promise async/await），都是通过类似 event loop 在平常的工作中已经足够，但是如果做复杂运算，这些异步伪线程的不足就逐渐体现出来，比如 setTimeout 拿到的值并不正确，再者假如页面有复杂运算的时候页面很容易触发假死状态，为了有多线程功能，webworker 问世了。不过，这并不意味着 JavaScript 语言本身就支持了多线程，对于 JavaScript 语言本身它仍是运行在单线程上的， Web Worker 只是浏览器（宿主环境）提供的一个能力／API。</p>
<p><img src="http://xiezixi.oschina.io/blog/images/webWorker1.png" alt="JavaScript 事件机制"></p>
<h3 id="1-3-Web-Worker-类型"><a href="#1-3-Web-Worker-类型" class="headerlink" title="1.3. Web Worker 类型"></a>1.3. Web Worker 类型</h3><p>Web workers 可分为两种类型：专用线程 dedicated web worker，以及共享线程 shared web worker。Dedicated web worker 随当前页面的关闭而结束；这意味着Dedicated web worker 只能被创建它的页面访问。与之相对应的Shared web worker 可以被多个页面访问。在 Javascript 代码中，<code>Work</code>类型代表 Dedicated web worker，而<code>SharedWorker</code>类型代表 Shared web worker。<br>而 Shared Worker 则可以被多个页面所共享（同域情况下）。</p>
<h3 id="1-4-浏览器兼容性"><a href="#1-4-浏览器兼容性" class="headerlink" title="1.4. 浏览器兼容性"></a>1.4. 浏览器兼容性</h3><p><a href="https://caniuse.com/#search=Web%20Workers" target="_blank" rel="noopener">Web Worker 浏览器兼容性</a></p>
<h2 id="2-使用方法"><a href="#2-使用方法" class="headerlink" title="2. 使用方法"></a>2. 使用方法</h2><h3 id="2-1-创建-Web-Worker"><a href="#2-1-创建-Web-Worker" class="headerlink" title="2.1. 创建 Web Worker"></a>2.1. 创建 Web Worker</h3><p>Web Worker的创建是在主线程当中通过传入文件的url来实现的。如下所示：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> webWorker = <span class="keyword">new</span> Worker(<span class="string">"webWorker.js"</span>);</span><br></pre></td></tr></table></figure>
<h3 id="2-2-事件监听"><a href="#2-2-事件监听" class="headerlink" title="2.2. 事件监听"></a>2.2. 事件监听</h3><p>返回的是 webWorker 实例对象,该对象是主线程和其他线程的通讯桥梁<br>主线程和其他线程可以通过如下相关的API进行通讯 。</p>
<blockquote>
<p><code>onmessage</code>: 监听事件<br><code>postmessage</code>: 传送事件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//主线程 main.js</span></span><br><span class="line"><span class="keyword">var</span> worker = <span class="keyword">new</span> Worker(<span class="string">"worker.js"</span>);</span><br><span class="line">worker.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>)</span>&#123;</span><br><span class="line">    <span class="comment">// 主线程收到子线程的消息</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 主线程向子线程发送消息</span></span><br><span class="line">worker.postMessage(&#123;</span><br><span class="line">    type: <span class="string">"start"</span>,</span><br><span class="line">    value: <span class="number">12345</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//web worker.js</span></span><br><span class="line">onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>)</span>&#123;</span><br><span class="line">   <span class="comment">// 收到</span></span><br><span class="line">&#125;;</span><br><span class="line">postMessage(&#123;</span><br><span class="line">    type: <span class="string">"debug"</span>,</span><br><span class="line">    message: <span class="string">"Starting processing..."</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="2-3-终止-Web-Worker"><a href="#2-3-终止-Web-Worker" class="headerlink" title="2.3. 终止 Web Worker"></a>2.3. 终止 Web Worker</h3><p>如果在某个时机不想要 Worker 继续运行了，那么我们需要终止掉这个线程，可以调用 在主线程 worker 的 <code>terminate</code> 方法或者在相应的线程中调用 <code>close</code>：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方式一 main.js 在主线程停止方式</span></span><br><span class="line"><span class="keyword">var</span> worker = <span class="keyword">new</span> Worker(<span class="string">'./worker.js'</span>);</span><br><span class="line">...</span><br><span class="line">worker.terminate();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式二、worker.js</span></span><br><span class="line">self.close();</span><br></pre></td></tr></table></figure>
<h3 id="2-4-错误机制"><a href="#2-4-错误机制" class="headerlink" title="2.4. 错误机制"></a>2.4. 错误机制</h3><p>提供了<code>onerror</code> API</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">worker.addEventListener(<span class="string">'error'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'MAIN: '</span>, <span class="string">'ERROR'</span>, e);</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'filename:'</span> + e.filename + <span class="string">'-message:'</span> + e.message + <span class="string">'-lineno:'</span> + e.lineno);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// event.filename: 导致错误的 Worker 脚本的名称；</span></span><br><span class="line"><span class="comment">// event.message: 错误的信息；</span></span><br><span class="line"><span class="comment">// event.lineno: 出现错误的行号；</span></span><br></pre></td></tr></table></figure>
<h3 id="2-5-简单实例"><a href="#2-5-简单实例" class="headerlink" title="2.5. 简单实例"></a>2.5. 简单实例</h3><h4 id="2-5-1-webWork-html"><a href="#2-5-1-webWork-html" class="headerlink" title="2.5.1 webWork.html"></a>2.5.1 webWork.html</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>webWorker<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">p</span>&gt;</span>计数：</span><br><span class="line">    <span class="tag">&lt;<span class="name">output</span> <span class="attr">id</span>=<span class="string">"result"</span>&gt;</span><span class="tag">&lt;/<span class="name">output</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"startWorker()"</span>&gt;</span>开始工作<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"stopWorker()"</span>&gt;</span>停止工作<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="javascript">    <span class="keyword">var</span> webWorker;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript">    <span class="function"><span class="keyword">function</span> <span class="title">startWorker</span>(<span class="params"></span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">        <span class="keyword">if</span> (<span class="keyword">typeof</span>(Worker) !== <span class="string">"undefined"</span>) &#123;</span></span><br><span class="line"><span class="javascript">            <span class="keyword">if</span> (<span class="keyword">typeof</span>(webWorker) == <span class="string">"undefined"</span>) &#123;</span></span><br><span class="line"><span class="javascript">                webWorker = <span class="keyword">new</span> Worker(<span class="string">"webWorker.js"</span>);</span></span><br><span class="line"><span class="undefined">            &#125;</span></span><br><span class="line"><span class="javascript">            webWorker.onmessage = <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">                <span class="built_in">document</span>.getElementById(<span class="string">"result"</span>).innerHTML = event.data;</span></span><br><span class="line"><span class="undefined">            &#125;;</span></span><br><span class="line"><span class="javascript">        &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">"result"</span>).innerHTML = <span class="string">"抱歉，你的浏览器不支持 Web Workers..."</span>;</span></span><br><span class="line"><span class="undefined">        &#125;</span></span><br><span class="line"><span class="undefined">    &#125;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript">    <span class="function"><span class="keyword">function</span> <span class="title">stopWorker</span>(<span class="params"></span>) </span>&#123;</span></span><br><span class="line"><span class="undefined">        webWorker.terminate();</span></span><br><span class="line"><span class="javascript">        webWorker = <span class="literal">undefined</span>;</span></span><br><span class="line"><span class="undefined">    &#125;</span></span><br><span class="line"><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h4 id="2-5-2-webWork-js"><a href="#2-5-2-webWork-js" class="headerlink" title="2.5.2 webWork.js"></a>2.5.2 webWork.js</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> i=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">timedCount</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    i=i+<span class="number">1</span>;</span><br><span class="line">    postMessage(i);</span><br><span class="line">    setTimeout(<span class="string">"timedCount()"</span>,<span class="number">500</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">timedCount();</span><br></pre></td></tr></table></figure>
<h2 id="3-sharedWorker"><a href="#3-sharedWorker" class="headerlink" title="3. sharedWorker"></a>3. sharedWorker</h2><h3 id="3-1-sharedWorker-介绍"><a href="#3-1-sharedWorker-介绍" class="headerlink" title="3.1. sharedWorker 介绍"></a>3.1. sharedWorker 介绍</h3><p>对于 Web Worker ，一个 tab 页面只能对应一个 Worker 线程，是相互独立的；<br>而 SharedWorker 提供了能力能够让不同标签中页面共享的同一个 Worker 脚本线程；<br>当然，有个很重要的限制就是它们需要满足同源策略，也就是需要在同域下；<br>在页面（可以多个）中实例化 Worker 线程：</p>
<h3 id="3-2-浏览器兼容性"><a href="#3-2-浏览器兼容性" class="headerlink" title="3.2. 浏览器兼容性"></a>3.2. 浏览器兼容性</h3><p><a href="https://caniuse.com/#search=sharedWorker" target="_blank" rel="noopener">sharedWorker 浏览器兼容性</a></p>
<h3 id="3-3-简单实例"><a href="#3-3-简单实例" class="headerlink" title="3.3. 简单实例"></a>3.3. 简单实例</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> myWorker = <span class="keyword">new</span> SharedWorker(<span class="string">"worker.js"</span>);</span><br><span class="line"></span><br><span class="line">myWorker.port.start();</span><br><span class="line"></span><br><span class="line">myWorker.port.postMessage(<span class="string">"hello, I'm main"</span>);</span><br><span class="line"></span><br><span class="line">myWorker.port.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'Message received from worker'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// worker.js</span></span><br><span class="line">onconnect = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> port = e.ports[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">  port.addEventListener(<span class="string">'message'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> workerResult = <span class="string">'Result: '</span> + (e.data[<span class="number">0</span>]);</span><br><span class="line">    port.postMessage(workerResult);</span><br><span class="line">  &#125;);</span><br><span class="line">  port.start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="4-环境与作用域"><a href="#4-环境与作用域" class="headerlink" title="4. 环境与作用域"></a>4. 环境与作用域</h2><p>在 Worker 线程的运行环境中没有 <code>window</code> 全局对象，也无法访问 <code>DOM</code> 对象，所以一般来说他只能来执行纯 JavaScript 的计算操作。但是，他还是可以获取到部分浏览器提供的 API 的：</p>
<p><code>setTimeout()</code>， <code>clearTimeout()</code>， <code>setInterval()</code>，<code>clearInterval()</code>：有了这些函数，就可以在 Worker : 线程中可以再创建 worker；<code>XMLHttpRequest</code> 对象：意味着我们可以在 Worker 线程中执行 <code>ajax</code> 请求；<code>navigator</code> 对象：可以获取到 <code>AppName</code>，<code>appVersion</code>，<code>platform</code>，<code>userAgent</code> 等信息；<code>location</code> 对象（只读）：可以获取到有关当前 URL 的信息；<code>Application Cache</code>、<code>indexedDB</code>、<code>WebSocket</code>、<code>Promise</code>。</p>
<h2 id="5-库或外部脚本引入和访问"><a href="#5-库或外部脚本引入和访问" class="headerlink" title="5. 库或外部脚本引入和访问"></a>5. 库或外部脚本引入和访问</h2><p>在线程中，提供了 <code>importScripts</code> 方法，如果线程中使用了 <code>importScripts</code> 一般按照以下步骤解析。</p>
<ol>
<li>解析 <code>importScripts</code> 方法的每一个参数</li>
<li>如果有任何失败或者错误，抛出 <code>SYNTAX_ERR</code> 异常</li>
<li>尝试从用户提供的 URL 资源位置处获取脚本资源</li>
<li>对于 <code>importScripts</code> 方法的每一个参数，按照用户的提供顺序，获取脚本资源后继续进行其它操作</li>
</ol>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// worker.js</span></span><br><span class="line">importScripts(<span class="string">'math_utilities.js'</span>);</span><br><span class="line">onmessage = <span class="function"><span class="keyword">function</span> (<span class="params">event</span>)</span></span><br><span class="line"><span class="function"> </span>&#123;</span><br><span class="line">     <span class="keyword">var</span> first=event.data.first;</span><br><span class="line">     <span class="keyword">var</span> second=event.data.second;</span><br><span class="line">     calculate(first,second); <span class="comment">// calculate 是math_utilities.js中的方法</span></span><br><span class="line"> &#125;;</span><br></pre></td></tr></table></figure>
<p>也可以一次性引入多个脚本：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//可以多起一次传入</span></span><br><span class="line">importScripts(<span class="string">'script1.js'</span>, <span class="string">'script2.js'</span>);</span><br></pre></td></tr></table></figure>
<h2 id="6-通讯原理"><a href="#6-通讯原理" class="headerlink" title="6. 通讯原理"></a>6. 通讯原理</h2><p>从一个线程到另一个线程的通讯实际上是一个值拷贝的过程，实际上是先将数据<code>JSON.stringify</code>之后再<code>JSON.parse</code>。主线程与子线程之间也可以交换二进制数据，比如<code>File</code>、<code>Blob</code>、<code>ArrayBuffer</code>等对象，也可以在线程之间发送。但是，用拷贝方式发送二进制数据，会造成性能问题。比如，主线程向子线程发送一个 50MB 文件，默认情况下浏览器会生成一个原文件的拷贝。为了解决这个问题，JavaScript 允许主线程把二进制数据直接转移给子线程，转移后主线程无法再使用这些数据，这是为了防止出现多个线程同时修改数据的问题，这种转移数据的方法，叫做<code>Transferable Objects</code>。</p>
<p>不过现在很多浏览器支持<code>transferable objects</code>(可转让对象) ,这个技术是零拷贝转移，能大大提升性能，可以指定传送的数据全都是零拷贝。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">var abBuffer = new ArrayBuffer(32);</span><br><span class="line">aDedicatedWorker.postMessage(abBuffer, [abBuffer]);</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">var objData = &#123;</span><br><span class="line">   &quot;employeeId&quot;: 103,</span><br><span class="line">   &quot;name&quot;: &quot;Sam Smith&quot;,</span><br><span class="line">   &quot;dateHired&quot;: new Date(2006, 11, 15),</span><br><span class="line">   &quot;abBuffer&quot;: new ArrayBuffer(32)</span><br><span class="line">&#125;;</span><br><span class="line">aDedicatedWorker.postMessage(objData, [objData.abBuffer]);</span><br></pre></td></tr></table></figure>
<h2 id="7-应用场景"><a href="#7-应用场景" class="headerlink" title="7. 应用场景"></a>7. 应用场景</h2><ol>
<li><strong>使用专用线程进行数学运算</strong><br>Web Worker 最简单的应用就是用来做后台计算，而这种计算并不会中断前台用户的操作</li>
<li><strong>图像处理</strong><br>通过使用从 <code>&lt;canvas&gt;</code> 或者 <code>&lt;video&gt;</code> 元素中获取的数据，可以把图像分割成几个不同的区域并且把它们推送给并行的不同 Workers 来做计算</li>
<li><strong>大量数据的检索</strong><br>当需要在调用 <code>ajax</code> 后处理大量的数据，如果处理这些数据所需的时间长短非常重要，可以在 Web Worker 中来做这些，避免冻结 UI 线程</li>
<li><strong>背景数据分析</strong><br>由于在使用 Web Worker 的时候，我们有更多潜在的 CPU 可用时间，我们现在可以考虑一下 JavaScript 中的新应用场景。例如，我们可以想像在不影响 UI 体验的情况下实时处理用户输入。利用这样一种可能，我们可以想像一个像 Word（Office Web Apps 套装）一样的应用：当用户打字时后台在词典中进行查找，帮助用户自动纠错等等</li>
</ol>
<h2 id="8-限制"><a href="#8-限制" class="headerlink" title="8. 限制"></a>8. 限制</h2><ol>
<li>兼容性</li>
<li>不能访问 <code>DOM</code> 和 <code>BOM</code> 对象，<code>location</code> 和 <code>navigator</code> 的只读访问，并且 <code>navigator</code> 封装成了 <code>WorkerNavigator</code> 对象，更改部分属性。无法读取本地文件系统</li>
<li>子线程和父级线程的通讯是通过值拷贝，子线程对通信内容的修改，不会影响到主线程。在通讯过程中值过大也会影响到性能（解决这个问题可以用<code>transferable objects</code>）</li>
<li>并非真的多线程，多线程是因为浏览器的功能</li>
<li>条数限制，大多浏览器能创建 webWorker 线程的条数是有限制的，虽然可以手动去拓展，但是如果不设置的话，基本上都在20条以内，每条线程大概5M左右，需要手动关掉一些不用的线程才能够创建新的线程</li>
</ol>
<h2 id="9-优秀开源框架"><a href="#9-优秀开源框架" class="headerlink" title="9. 优秀开源框架"></a>9. 优秀开源框架</h2><p><a href="https://github.com/DoubleSpout/node-threads-a-gogo2" target="_blank" rel="noopener">tagg2</a></p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
  </section>

  
  <nav class="pagination">
    <span class="page-number current">1</span><a class="page-number" href="/blog/page/2/">2</a><a class="extend next" rel="next" href="/blog/page/2/"><i class="fa fa-angle-right"></i></a>
  </nav>



          </div>
          


          

        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      

      <section class="site-overview sidebar-panel sidebar-panel-active">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/blog/images/avatar.jpg"
               alt="曦瓜皮" />
          <p class="site-author-name" itemprop="name">曦瓜皮</p>
           
              <p class="site-description motion-element" itemprop="description"></p>
          
        </div>
        <nav class="site-state motion-element">

          
            <div class="site-state-item site-state-posts">
              <a href="/blog/archives/">
                <span class="site-state-item-count">15</span>
                <span class="site-state-item-name">日志</span>
              </a>
            </div>
          

          
            
            
            <div class="site-state-item site-state-categories">
              <a href="/blog/categories/index.html">
                <span class="site-state-item-count">6</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          

        </nav>

        

        <div class="links-of-author motion-element">
          
        </div>

        
        

        
        

        

      </section>

      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy; 
  <span itemprop="copyrightYear">2018</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">曦瓜皮</span>
</div>


<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Muse
  </a>
</div>


        

        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.2"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.2"></script>



  
  

  
    <script type="text/javascript" src="/blog/js/src/scrollspy.js?v=5.1.2"></script>
<script type="text/javascript" src="/blog/js/src/post-details.js?v=5.1.2"></script>

  

  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.2"></script>



  


  




	





  





  






  





  

  

  

  

  

  

</body>
</html>
